From 897b2765d6c7381566d76fed8d13fa0be1914e06 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 8 May 2024 16:10:14 +0800 Subject: [PATCH] add yoga layout engine. --- Assets/LICENSES | 24 + Assets/Script/Dev/Entry.lua | 4 +- Assets/Script/Dev/Entry.yue | 4 +- Assets/Script/Example/Sprite.lua | 243 +- Assets/Script/Example/Sprite.yue | 7 +- Assets/Script/Lib/Dora/en/AlignNode.d.tl | 58 + Assets/Script/Lib/Dora/en/DragonBone.d.tl | 3 - Assets/Script/Lib/Dora/en/Node.d.tl | 8 + .../Script/Lib/Dora/en/PhysicsWorldType.d.tl | 3 - Assets/Script/Lib/Dora/en/Spine.d.tl | 3 - Assets/Script/Lib/Dora/en/dora.d.ts | 74 +- Assets/Script/Lib/Dora/en/jsx.d.ts | 104 +- Assets/Script/Lib/Dora/zh-Hans/AlignNode.d.tl | 55 + .../Script/Lib/Dora/zh-Hans/DragonBone.d.tl | 3 - Assets/Script/Lib/Dora/zh-Hans/Node.d.tl | 8 + .../Lib/Dora/zh-Hans/PhysicsWorldType.d.tl | 3 - Assets/Script/Lib/Dora/zh-Hans/Spine.d.tl | 3 - Assets/Script/Lib/Dora/zh-Hans/dora.d.ts | 71 + Assets/Script/Lib/Dora/zh-Hans/jsx.d.ts | 101 +- Assets/Script/Lib/WebServer.lua | 482 +-- Assets/Script/Lib/WebServer.yue | 5 +- Assets/Script/Lib/dora-x.lua | 2951 +++++++++-------- Assets/Script/Test/LayoutTS.lua | 17 + Assets/Script/Test/LayoutTS.ts | 22 + Assets/Script/Test/LayoutTSX.lua | 236 ++ Assets/Script/Test/LayoutTSX.tsx | 327 ++ Assets/Script/Test/SpriteTL.lua | 8 +- Assets/Script/Test/SpriteTL.tl | 8 +- LICENSES.3rdparty.md | 1 + Project/Android/Dora/app/CMakeLists.txt | 20 + Project/Android/Dora/app/build.gradle | 4 +- Project/Linux/CMakeLists.txt | 20 + Project/Windows/Dora/Dora.vcxproj | 131 + Project/Windows/Dora/Dora.vcxproj.filters | 249 ++ Project/Windows/Dora/Resource.rc | 8 +- Project/iOS/Dora.xcodeproj/project.pbxproj | 300 ++ Project/iOS/Dora/Info.plist | 4 +- Project/macOS/Dora.xcodeproj/project.pbxproj | 261 ++ Project/macOS/Dora/Info.plist | 4 +- Source/3rdParty/yoga/LICENSE | 21 + Source/3rdParty/yoga/YGConfig.cpp | 92 + Source/3rdParty/yoga/YGConfig.h | 158 + Source/3rdParty/yoga/YGEnums.cpp | 250 ++ Source/3rdParty/yoga/YGEnums.h | 136 + Source/3rdParty/yoga/YGMacros.h | 93 + Source/3rdParty/yoga/YGNode.cpp | 366 ++ Source/3rdParty/yoga/YGNode.h | 304 ++ Source/3rdParty/yoga/YGNodeLayout.cpp | 92 + Source/3rdParty/yoga/YGNodeLayout.h | 35 + Source/3rdParty/yoga/YGNodeStyle.cpp | 390 +++ Source/3rdParty/yoga/YGNodeStyle.h | 125 + Source/3rdParty/yoga/YGPixelGrid.cpp | 22 + Source/3rdParty/yoga/YGPixelGrid.h | 29 + Source/3rdParty/yoga/YGValue.cpp | 20 + Source/3rdParty/yoga/YGValue.h | 84 + Source/3rdParty/yoga/Yoga.h | 21 + .../yoga/algorithm/AbsoluteLayout.cpp | 598 ++++ .../3rdParty/yoga/algorithm/AbsoluteLayout.h | 40 + Source/3rdParty/yoga/algorithm/Align.h | 72 + Source/3rdParty/yoga/algorithm/Baseline.cpp | 82 + Source/3rdParty/yoga/algorithm/Baseline.h | 21 + Source/3rdParty/yoga/algorithm/BoundAxis.h | 72 + Source/3rdParty/yoga/algorithm/Cache.cpp | 121 + Source/3rdParty/yoga/algorithm/Cache.h | 30 + .../yoga/algorithm/CalculateLayout.cpp | 2353 +++++++++++++ .../3rdParty/yoga/algorithm/CalculateLayout.h | 38 + .../3rdParty/yoga/algorithm/FlexDirection.h | 120 + Source/3rdParty/yoga/algorithm/FlexLine.cpp | 123 + Source/3rdParty/yoga/algorithm/FlexLine.h | 77 + Source/3rdParty/yoga/algorithm/PixelGrid.cpp | 132 + Source/3rdParty/yoga/algorithm/PixelGrid.h | 29 + Source/3rdParty/yoga/algorithm/SizingMode.h | 73 + .../yoga/algorithm/TrailingPosition.h | 44 + Source/3rdParty/yoga/config/Config.cpp | 116 + Source/3rdParty/yoga/config/Config.h | 89 + Source/3rdParty/yoga/enums/Align.h | 47 + Source/3rdParty/yoga/enums/Dimension.h | 40 + Source/3rdParty/yoga/enums/Direction.h | 41 + Source/3rdParty/yoga/enums/Display.h | 40 + Source/3rdParty/yoga/enums/Edge.h | 47 + Source/3rdParty/yoga/enums/Errata.h | 41 + .../3rdParty/yoga/enums/ExperimentalFeature.h | 39 + Source/3rdParty/yoga/enums/FlexDirection.h | 42 + Source/3rdParty/yoga/enums/Gutter.h | 41 + Source/3rdParty/yoga/enums/Justify.h | 44 + Source/3rdParty/yoga/enums/LogLevel.h | 44 + Source/3rdParty/yoga/enums/MeasureMode.h | 41 + Source/3rdParty/yoga/enums/NodeType.h | 40 + Source/3rdParty/yoga/enums/Overflow.h | 41 + Source/3rdParty/yoga/enums/PhysicalEdge.h | 21 + Source/3rdParty/yoga/enums/PositionType.h | 41 + Source/3rdParty/yoga/enums/Unit.h | 42 + Source/3rdParty/yoga/enums/Wrap.h | 41 + Source/3rdParty/yoga/enums/YogaEnums.h | 85 + Source/3rdParty/yoga/event/event.cpp | 87 + Source/3rdParty/yoga/event/event.h | 130 + Source/3rdParty/yoga/node/CachedMeasurement.h | 53 + Source/3rdParty/yoga/node/LayoutResults.cpp | 47 + Source/3rdParty/yoga/node/LayoutResults.h | 121 + Source/3rdParty/yoga/node/Node.cpp | 361 ++ Source/3rdParty/yoga/node/Node.h | 299 ++ Source/3rdParty/yoga/numeric/Comparison.h | 81 + Source/3rdParty/yoga/numeric/FloatOptional.h | 93 + Source/3rdParty/yoga/style/SmallValueBuffer.h | 133 + Source/3rdParty/yoga/style/Style.h | 676 ++++ Source/3rdParty/yoga/style/StyleLength.h | 139 + Source/3rdParty/yoga/style/StyleValueHandle.h | 98 + Source/3rdParty/yoga/style/StyleValuePool.h | 127 + Source/Basic/Application.cpp | 2 +- Source/Common/Utils.h | 7 + Source/Dora.h | 1 + Source/Node/AlignNode.cpp | 833 +++++ Source/Node/AlignNode.h | 38 + Source/Node/DragonBone.cpp | 5 +- Source/Node/DragonBone.h | 4 +- Source/Node/DrawNode.cpp | 41 + Source/Node/DrawNode.h | 2 + Source/Node/Node.cpp | 58 +- Source/Node/Node.h | 5 +- Source/Node/Spine.cpp | 5 +- Source/Node/Spine.h | 2 +- Source/Physics/PhysicsWorld.cpp | 5 +- Source/Physics/PhysicsWorld.h | 3 +- Source/Shader/Draw/fs_draw.bin.h | 163 +- Source/Shader/Draw/fs_draw.sc | 7 +- Source/Wasm/Dora/AlignNodeWasm.hpp | 22 + Source/Wasm/Dora/DragonBoneWasm.hpp | 8 - Source/Wasm/Dora/NodeWasm.hpp | 8 + Source/Wasm/Dora/PhysicsWorldWasm.hpp | 8 - Source/Wasm/Dora/SpineWasm.hpp | 8 - Source/Wasm/WasmRuntime.cpp | 2 + Tools/RustWasmGen/Dora.h | 50 +- Tools/dora-jsx/dora-x.ts | 71 +- Tools/dora-rust/dora-test/Cargo.lock | 2 +- Tools/dora-rust/dora-test/src/main.rs | 4 + Tools/dora-rust/dora-test/src/tests.rs | 3 +- Tools/dora-rust/dora-test/src/tests/layout.rs | 51 + Tools/dora-rust/dora/Cargo.lock | 2 +- Tools/dora-rust/dora/Cargo.toml | 2 +- Tools/dora-rust/dora/src/dora.rs | 3 + Tools/dora-rust/dora/src/dora/align_node.rs | 70 + Tools/dora-rust/dora/src/dora/dragon_bone.rs | 10 - Tools/dora-rust/dora/src/dora/node.rs | 10 + .../dora-rust/dora/src/dora/physics_world.rs | 10 - Tools/dora-rust/dora/src/dora/spine.rs | 10 - Tools/tolua++/Dora.h | 10 +- Tools/tolua++/basic.lua | 1 + 147 files changed, 15061 insertions(+), 2053 deletions(-) create mode 100644 Assets/Script/Lib/Dora/en/AlignNode.d.tl create mode 100644 Assets/Script/Lib/Dora/zh-Hans/AlignNode.d.tl create mode 100644 Assets/Script/Test/LayoutTS.lua create mode 100644 Assets/Script/Test/LayoutTS.ts create mode 100644 Assets/Script/Test/LayoutTSX.lua create mode 100644 Assets/Script/Test/LayoutTSX.tsx create mode 100644 Source/3rdParty/yoga/LICENSE create mode 100644 Source/3rdParty/yoga/YGConfig.cpp create mode 100644 Source/3rdParty/yoga/YGConfig.h create mode 100644 Source/3rdParty/yoga/YGEnums.cpp create mode 100644 Source/3rdParty/yoga/YGEnums.h create mode 100644 Source/3rdParty/yoga/YGMacros.h create mode 100644 Source/3rdParty/yoga/YGNode.cpp create mode 100644 Source/3rdParty/yoga/YGNode.h create mode 100644 Source/3rdParty/yoga/YGNodeLayout.cpp create mode 100644 Source/3rdParty/yoga/YGNodeLayout.h create mode 100644 Source/3rdParty/yoga/YGNodeStyle.cpp create mode 100644 Source/3rdParty/yoga/YGNodeStyle.h create mode 100644 Source/3rdParty/yoga/YGPixelGrid.cpp create mode 100644 Source/3rdParty/yoga/YGPixelGrid.h create mode 100644 Source/3rdParty/yoga/YGValue.cpp create mode 100644 Source/3rdParty/yoga/YGValue.h create mode 100644 Source/3rdParty/yoga/Yoga.h create mode 100644 Source/3rdParty/yoga/algorithm/AbsoluteLayout.cpp create mode 100644 Source/3rdParty/yoga/algorithm/AbsoluteLayout.h create mode 100644 Source/3rdParty/yoga/algorithm/Align.h create mode 100644 Source/3rdParty/yoga/algorithm/Baseline.cpp create mode 100644 Source/3rdParty/yoga/algorithm/Baseline.h create mode 100644 Source/3rdParty/yoga/algorithm/BoundAxis.h create mode 100644 Source/3rdParty/yoga/algorithm/Cache.cpp create mode 100644 Source/3rdParty/yoga/algorithm/Cache.h create mode 100644 Source/3rdParty/yoga/algorithm/CalculateLayout.cpp create mode 100644 Source/3rdParty/yoga/algorithm/CalculateLayout.h create mode 100644 Source/3rdParty/yoga/algorithm/FlexDirection.h create mode 100644 Source/3rdParty/yoga/algorithm/FlexLine.cpp create mode 100644 Source/3rdParty/yoga/algorithm/FlexLine.h create mode 100644 Source/3rdParty/yoga/algorithm/PixelGrid.cpp create mode 100644 Source/3rdParty/yoga/algorithm/PixelGrid.h create mode 100644 Source/3rdParty/yoga/algorithm/SizingMode.h create mode 100644 Source/3rdParty/yoga/algorithm/TrailingPosition.h create mode 100644 Source/3rdParty/yoga/config/Config.cpp create mode 100644 Source/3rdParty/yoga/config/Config.h create mode 100644 Source/3rdParty/yoga/enums/Align.h create mode 100644 Source/3rdParty/yoga/enums/Dimension.h create mode 100644 Source/3rdParty/yoga/enums/Direction.h create mode 100644 Source/3rdParty/yoga/enums/Display.h create mode 100644 Source/3rdParty/yoga/enums/Edge.h create mode 100644 Source/3rdParty/yoga/enums/Errata.h create mode 100644 Source/3rdParty/yoga/enums/ExperimentalFeature.h create mode 100644 Source/3rdParty/yoga/enums/FlexDirection.h create mode 100644 Source/3rdParty/yoga/enums/Gutter.h create mode 100644 Source/3rdParty/yoga/enums/Justify.h create mode 100644 Source/3rdParty/yoga/enums/LogLevel.h create mode 100644 Source/3rdParty/yoga/enums/MeasureMode.h create mode 100644 Source/3rdParty/yoga/enums/NodeType.h create mode 100644 Source/3rdParty/yoga/enums/Overflow.h create mode 100644 Source/3rdParty/yoga/enums/PhysicalEdge.h create mode 100644 Source/3rdParty/yoga/enums/PositionType.h create mode 100644 Source/3rdParty/yoga/enums/Unit.h create mode 100644 Source/3rdParty/yoga/enums/Wrap.h create mode 100644 Source/3rdParty/yoga/enums/YogaEnums.h create mode 100644 Source/3rdParty/yoga/event/event.cpp create mode 100644 Source/3rdParty/yoga/event/event.h create mode 100644 Source/3rdParty/yoga/node/CachedMeasurement.h create mode 100644 Source/3rdParty/yoga/node/LayoutResults.cpp create mode 100644 Source/3rdParty/yoga/node/LayoutResults.h create mode 100644 Source/3rdParty/yoga/node/Node.cpp create mode 100644 Source/3rdParty/yoga/node/Node.h create mode 100644 Source/3rdParty/yoga/numeric/Comparison.h create mode 100644 Source/3rdParty/yoga/numeric/FloatOptional.h create mode 100644 Source/3rdParty/yoga/style/SmallValueBuffer.h create mode 100644 Source/3rdParty/yoga/style/Style.h create mode 100644 Source/3rdParty/yoga/style/StyleLength.h create mode 100644 Source/3rdParty/yoga/style/StyleValueHandle.h create mode 100644 Source/3rdParty/yoga/style/StyleValuePool.h create mode 100644 Source/Node/AlignNode.cpp create mode 100644 Source/Node/AlignNode.h create mode 100644 Source/Wasm/Dora/AlignNodeWasm.hpp create mode 100644 Tools/dora-rust/dora-test/src/tests/layout.rs create mode 100644 Tools/dora-rust/dora/src/dora/align_node.rs diff --git a/Assets/LICENSES b/Assets/LICENSES index 9574c0d79..1acfafe72 100644 --- a/Assets/LICENSES +++ b/Assets/LICENSES @@ -1353,3 +1353,27 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------- + +Yoga: MIT License + +Copyright (c) Facebook, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Assets/Script/Dev/Entry.lua b/Assets/Script/Dev/Entry.lua index 83dbbf438..f7fd8186f 100644 --- a/Assets/Script/Dev/Entry.lua +++ b/Assets/Script/Dev/Entry.lua @@ -457,8 +457,8 @@ doCompile = function(minify) -- 256 } -- 265 for _index_0 = 1, #gamesInDev do -- 272 local _des_0 = gamesInDev[_index_0] -- 272 - local name, entryFile = _des_0[1], _des_0[2] -- 272 - local gamePath = Path:getPath(entryFile) -- 273 + local entryFile = _des_0[2] -- 272 + local gamePath = Path:getPath(Path:getRelative(entryFile, writablePath)) -- 273 buildPaths[#buildPaths + 1] = { -- 275 Path(writablePath, gamePath), -- 275 Path(writablePath, ".build", gamePath), -- 276 diff --git a/Assets/Script/Dev/Entry.yue b/Assets/Script/Dev/Entry.yue index ae4dfc7df..ed1f22119 100644 --- a/Assets/Script/Dev/Entry.yue +++ b/Assets/Script/Dev/Entry.yue @@ -269,8 +269,8 @@ doCompile = (minify) -> "" ], ] - for [name, entryFile] in *gamesInDev - gamePath = Path\getPath entryFile + for [_, entryFile] in *gamesInDev + gamePath = Path\getPath Path\getRelative entryFile, writablePath buildPaths[] = * Path writablePath, gamePath * Path writablePath, ".build", gamePath diff --git a/Assets/Script/Example/Sprite.lua b/Assets/Script/Example/Sprite.lua index e1c4bc39c..2a077ae9f 100644 --- a/Assets/Script/Example/Sprite.lua +++ b/Assets/Script/Example/Sprite.lua @@ -12,127 +12,134 @@ do -- 3 local _with_0 = Sprite("Image/logo.png") -- 3 _with_0.scaleX = 0.5 -- 4 _with_0.scaleY = 0.5 -- 4 + _with_0.showDebug = true -- 5 sprite = _with_0 -- 3 end -- 3 -do -- 6 - local _with_0 = Node() -- 6 - _with_0.touchEnabled = true -- 7 - _with_0:slot("TapMoved", function(touch) -- 8 - if not touch.first then -- 9 - return -- 9 - end -- 9 - sprite.position = sprite.position + touch.delta -- 10 - end) -- 8 - _with_0:addChild(sprite) -- 11 -end -- 6 -local windowFlags = { -- 16 - "NoResize", -- 16 - "NoSavedSettings" -- 17 -} -- 15 -return threadLoop(function() -- 18 - local width -- 19 - width = App.visualSize.width -- 19 - ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 20 - ImGui.SetNextWindowSize(Vec2(240, 520), "FirstUseEver") -- 21 - return ImGui.Begin("Sprite", windowFlags, function() -- 22 - ImGui.Text("Sprite (Yuescript)") -- 23 - ImGui.BeginChild("SpriteSetting", Vec2(-1, -40), function() -- 24 - local z = sprite.z -- 25 - do -- 26 - local changed -- 26 - changed, z = ImGui.DragFloat("Z", z, 1, -1000, 1000, "%.2f") -- 26 - if changed then -- 26 - sprite.z = z -- 27 - end -- 26 - end -- 26 - local x, y -- 28 - do -- 28 - local _obj_0 = sprite.anchor -- 28 - x, y = _obj_0.x, _obj_0.y -- 28 - end -- 28 +do -- 7 + local _with_0 = Node() -- 7 + _with_0.touchEnabled = true -- 8 + _with_0:slot("TapMoved", function(touch) -- 9 + if not touch.first then -- 10 + return -- 10 + end -- 10 + sprite.position = sprite.position + touch.delta -- 11 + end) -- 9 + _with_0:addChild(sprite) -- 12 +end -- 7 +local windowFlags = { -- 17 + "NoResize", -- 17 + "NoSavedSettings" -- 18 +} -- 16 +return threadLoop(function() -- 19 + local width -- 20 + width = App.visualSize.width -- 20 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 21 + ImGui.SetNextWindowSize(Vec2(240, 520), "FirstUseEver") -- 22 + return ImGui.Begin("Sprite", windowFlags, function() -- 23 + ImGui.Text("Sprite (Yuescript)") -- 24 + ImGui.BeginChild("SpriteSetting", Vec2(-1, -40), function() -- 25 + local z = sprite.z -- 26 + do -- 27 + local changed -- 27 + changed, z = ImGui.DragFloat("Z", z, 1, -1000, 1000, "%.2f") -- 27 + if changed then -- 27 + sprite.z = z -- 28 + end -- 27 + end -- 27 + local x, y -- 29 do -- 29 - local changed -- 29 - changed, x, y = ImGui.DragFloat2("Anchor", x, y, 0.01, 0, 1, "%.2f") -- 29 - if changed then -- 29 - sprite.anchor = Vec2(x, y) -- 30 - end -- 29 + local _obj_0 = sprite.anchor -- 29 + x, y = _obj_0.x, _obj_0.y -- 29 end -- 29 - local spriteW, height -- 31 - do -- 31 - local _obj_0 = sprite.size -- 31 - spriteW, height = _obj_0.width, _obj_0.height -- 31 - end -- 31 + do -- 30 + local changed -- 30 + changed, x, y = ImGui.DragFloat2("Anchor", x, y, 0.01, 0, 1, "%.2f") -- 30 + if changed then -- 30 + sprite.anchor = Vec2(x, y) -- 31 + end -- 30 + end -- 30 + local spriteW, height -- 32 do -- 32 - local changed -- 32 - changed, spriteW, height = ImGui.DragFloat2("Size", spriteW, height, 0.1, 0, 1000, "%.f") -- 32 - if changed then -- 32 - sprite.size = Size(spriteW, height) -- 33 - end -- 32 + local _obj_0 = sprite.size -- 32 + spriteW, height = _obj_0.width, _obj_0.height -- 32 end -- 32 - local scaleX, scaleY = sprite.scaleX, sprite.scaleY -- 34 - do -- 35 - local changed -- 35 - changed, scaleX, scaleY = ImGui.DragFloat2("Scale", scaleX, scaleY, 0.01, -2, 2, "%.2f") -- 35 - if changed then -- 35 - sprite.scaleX, sprite.scaleY = scaleX, scaleY -- 36 - end -- 35 - end -- 35 - ImGui.PushItemWidth(-60, function() -- 37 - local angle = sprite.angle -- 38 - local changed -- 39 - changed, angle = ImGui.DragInt("Angle", math.floor(angle), 1, -360, 360) -- 39 - if changed then -- 39 - sprite.angle = angle -- 40 - end -- 39 - end) -- 37 - ImGui.PushItemWidth(-60, function() -- 41 - local angleX = sprite.angleX -- 42 - local changed -- 43 - changed, angleX = ImGui.DragInt("AngleX", math.floor(angleX), 1, -360, 360) -- 43 - if changed then -- 43 - sprite.angleX = angleX -- 44 - end -- 43 - end) -- 41 - ImGui.PushItemWidth(-60, function() -- 45 - local angleY = sprite.angleY -- 46 - local changed -- 47 - changed, angleY = ImGui.DragInt("AngleY", math.floor(angleY), 1, -360, 360) -- 47 - if changed then -- 47 - sprite.angleY = angleY -- 48 - end -- 47 - end) -- 45 - local skewX, skewY = sprite.skewX, sprite.skewY -- 49 - do -- 50 - local changed -- 50 - changed, skewX, skewY = ImGui.DragInt2("Skew", math.floor(skewX), math.floor(skewY), 1, -360, 360) -- 50 - if changed then -- 50 - sprite.skewX, sprite.skewY = skewX, skewY -- 51 - end -- 50 - end -- 50 - ImGui.PushItemWidth(-70, function() -- 52 - local opacity = sprite.opacity -- 53 - local changed -- 54 - changed, opacity = ImGui.DragFloat("Opacity", opacity, 0.01, 0, 1, "%.2f") -- 54 - if changed then -- 54 - sprite.opacity = opacity -- 55 - end -- 54 - end) -- 52 - return ImGui.PushItemWidth(-1, function() -- 56 - local color3 = sprite.color3 -- 57 - ImGui.SetColorEditOptions({ -- 58 - "DisplayRGB" -- 58 - }) -- 58 - if ImGui.ColorEdit3("", color3) then -- 59 - sprite.color3 = color3 -- 60 - end -- 59 - end) -- 60 - end) -- 24 - if ImGui.Button("Reset", Vec2(140, 30)) then -- 61 - local _with_0 = sprite.parent -- 62 - _with_0:removeChild(sprite) -- 63 - sprite = Sprite("Image/logo.png") -- 64 - _with_0:addChild(sprite) -- 65 - return _with_0 -- 62 - end -- 61 - end) -- 65 -end) -- 65 + do -- 33 + local changed -- 33 + changed, spriteW, height = ImGui.DragFloat2("Size", spriteW, height, 0.1, 0, 1500, "%.f") -- 33 + if changed then -- 33 + sprite.size = Size(spriteW, height) -- 34 + end -- 33 + end -- 33 + local scaleX, scaleY = sprite.scaleX, sprite.scaleY -- 35 + do -- 36 + local changed -- 36 + changed, scaleX, scaleY = ImGui.DragFloat2("Scale", scaleX, scaleY, 0.01, -2, 2, "%.2f") -- 36 + if changed then -- 36 + sprite.scaleX, sprite.scaleY = scaleX, scaleY -- 37 + end -- 36 + end -- 36 + ImGui.PushItemWidth(-60, function() -- 38 + local angle = sprite.angle -- 39 + local changed -- 40 + changed, angle = ImGui.DragInt("Angle", math.floor(angle), 1, -360, 360) -- 40 + if changed then -- 40 + sprite.angle = angle -- 41 + end -- 40 + end) -- 38 + ImGui.PushItemWidth(-60, function() -- 42 + local angleX = sprite.angleX -- 43 + local changed -- 44 + changed, angleX = ImGui.DragInt("AngleX", math.floor(angleX), 1, -360, 360) -- 44 + if changed then -- 44 + sprite.angleX = angleX -- 45 + end -- 44 + end) -- 42 + ImGui.PushItemWidth(-60, function() -- 46 + local angleY = sprite.angleY -- 47 + local changed -- 48 + changed, angleY = ImGui.DragInt("AngleY", math.floor(angleY), 1, -360, 360) -- 48 + if changed then -- 48 + sprite.angleY = angleY -- 49 + end -- 48 + end) -- 46 + local skewX, skewY = sprite.skewX, sprite.skewY -- 50 + do -- 51 + local changed -- 51 + changed, skewX, skewY = ImGui.DragInt2("Skew", math.floor(skewX), math.floor(skewY), 1, -360, 360) -- 51 + if changed then -- 51 + sprite.skewX, sprite.skewY = skewX, skewY -- 52 + end -- 51 + end -- 51 + ImGui.PushItemWidth(-70, function() -- 53 + local opacity = sprite.opacity -- 54 + local changed -- 55 + changed, opacity = ImGui.DragFloat("Opacity", opacity, 0.01, 0, 1, "%.2f") -- 55 + if changed then -- 55 + sprite.opacity = opacity -- 56 + end -- 55 + end) -- 53 + return ImGui.PushItemWidth(-1, function() -- 57 + local color3 = sprite.color3 -- 58 + ImGui.SetColorEditOptions({ -- 59 + "DisplayRGB" -- 59 + }) -- 59 + if ImGui.ColorEdit3("", color3) then -- 60 + sprite.color3 = color3 -- 61 + end -- 60 + end) -- 61 + end) -- 25 + if ImGui.Button("Reset", Vec2(140, 30)) then -- 62 + local _with_0 = sprite.parent -- 63 + _with_0:removeChild(sprite) -- 64 + do -- 65 + local _with_1 = Sprite("Image/logo.png") -- 65 + _with_1.scaleX = 0.5 -- 66 + _with_1.scaleY = 0.5 -- 66 + _with_1.showDebug = true -- 67 + sprite = _with_1 -- 65 + end -- 65 + _with_0:addChild(sprite) -- 68 + return _with_0 -- 63 + end -- 62 + end) -- 68 +end) -- 68 diff --git a/Assets/Script/Example/Sprite.yue b/Assets/Script/Example/Sprite.yue index 6fa2507bb..e46941a2e 100644 --- a/Assets/Script/Example/Sprite.yue +++ b/Assets/Script/Example/Sprite.yue @@ -2,6 +2,7 @@ _ENV = Dora! sprite = with Sprite "Image/logo.png" .scaleX = .scaleY = 0.5 + .showDebug = true with Node! .touchEnabled = true @@ -29,7 +30,7 @@ threadLoop -> if changed, x, y := ImGui.DragFloat2 "Anchor", x, y, 0.01, 0, 1, "%.2f" sprite.anchor = Vec2 x, y width: spriteW, :height = sprite.size - if changed, spriteW, height := ImGui.DragFloat2 "Size", spriteW, height, 0.1, 0, 1000, "%.f" + if changed, spriteW, height := ImGui.DragFloat2 "Size", spriteW, height, 0.1, 0, 1500, "%.f" sprite.size = Size spriteW, height :scaleX, :scaleY = sprite if changed, scaleX, scaleY := ImGui.DragFloat2 "Scale", scaleX, scaleY, 0.01, -2, 2, "%.2f" @@ -61,5 +62,7 @@ threadLoop -> if ImGui.Button "Reset", Vec2 140, 30 with sprite.parent \removeChild sprite - sprite = Sprite "Image/logo.png" + sprite = with Sprite "Image/logo.png" + .scaleX = .scaleY = 0.5 + .showDebug = true \addChild sprite diff --git a/Assets/Script/Lib/Dora/en/AlignNode.d.tl b/Assets/Script/Lib/Dora/en/AlignNode.d.tl new file mode 100644 index 000000000..acec33994 --- /dev/null +++ b/Assets/Script/Lib/Dora/en/AlignNode.d.tl @@ -0,0 +1,58 @@ +local Node = require("Node").Type + +-- AlignNode is a layout node that aligns its children. +local record AlignNode + + -- Inherits from `Node`. + embed Node + + -- Sets the layout style of the node. + -- @param style (string) The layout style. + -- The following properties can be set through a CSS style string: + -- + -- ## Layout direction and alignment + -- * direction: Sets the direction (ltr, rtl, inherit). + -- * align-items, align-self, align-content: Sets the alignment of different items (flex-start, center, stretch, flex-end, auto). + -- * flex-direction: Sets the layout direction (column, row, column-reverse, row-reverse). + -- * justify-content: Sets the arrangement of child items (flex-start, center, flex-end, space-between, space-around, space-evenly). + -- + -- ## Flex properties + -- * flex: Sets the overall size of the flex container. + -- * flex-grow: Sets the flex growth value. + -- * flex-shrink: Sets the flex shrink value. + -- * flex-wrap: Sets whether to wrap (nowrap, wrap, wrap-reverse). + -- * flex-basis: Sets the flex basis value or percentage. + -- + -- ## Margins and dimensions + -- * margin: Can be set by a single value or multiple values separated by commas, percentages or auto for each side. + -- * margin-top, margin-right, margin-bottom, margin-left, margin-start, margin-end: Sets the margin values, percentages or auto. + -- * padding: Can be set by a single value or multiple values separated by commas or percentages for each side. + -- * padding-top, padding-right, padding-bottom, padding-left: Sets the padding values or percentages. + -- * border: Can be set by a single value or multiple values separated by commas for each side. + -- * width, height, min-width, min-height, max-width, max-height: Sets the dimension values or percentage properties. + -- + -- ## Positioning + -- * top, right, bottom, left, start, end, horizontal, vertical: Sets the positioning property values or percentages. + -- + -- ## Other properties + -- * position: Sets the positioning type (absolute, relative, static). + -- * overflow: Sets the overflow property (visible, hidden, scroll). + -- * display: Controls whether to display (flex, none). + -- + -- @usage + -- alignNode:css("flex-direction: column; justify-content: center; align-items: center;") + css: function(self: AlignNode, style: string) +end + +-- A class for creating AlignNode objects. +local record AlignNodeClass + type Type = AlignNode + + -- Creates a new AlignNode. + -- @param isWindowRoot (boolean) [optional] Whether the node is a window root node. A window root node will automatically listen for window size change events and update the layout accordingly. + -- @return (AlignNode) The created AlignNode object. + metamethod __call: function(self: AlignNodeClass, isWindowRoot?: boolean): AlignNode +end + +local alignNodeClass: AlignNodeClass +return alignNodeClass diff --git a/Assets/Script/Lib/Dora/en/DragonBone.d.tl b/Assets/Script/Lib/Dora/en/DragonBone.d.tl index 60359a467..46fb197de 100644 --- a/Assets/Script/Lib/Dora/en/DragonBone.d.tl +++ b/Assets/Script/Lib/Dora/en/DragonBone.d.tl @@ -6,9 +6,6 @@ local record DragonBone -- Inherit from the `Playable`. embed Playable - -- Whether to show debug graphics. - showDebug: boolean - -- Whether hit testing is enabled. hitTestEnabled: boolean diff --git a/Assets/Script/Lib/Dora/en/Node.d.tl b/Assets/Script/Lib/Dora/en/Node.d.tl index 5ef816146..fc450d25f 100644 --- a/Assets/Script/Lib/Dora/en/Node.d.tl +++ b/Assets/Script/Lib/Dora/en/Node.d.tl @@ -142,6 +142,9 @@ local record Node -- Whether to group the node's rendering with all its recursive children. renderGroup: boolean + -- Whether debug graphic should be displayed for the node. + showDebug: boolean + -- The rendering order number for group rendering. Nodes with lower rendering orders are rendered earlier. renderOrder: integer @@ -567,6 +570,11 @@ local record __SLOT__ -- @param controllerId (integer) The controller id, incrementing from 0 when multiple controllers connected. -- @param axisValue (number) The controller axis value ranging from -1.0 to 1.0. ["Axis"]: function(controllerId: integer, axisValue: number) + + -- Triggers when the layout of the `AlignNode` is updated. + -- @param width The width of the node. + -- @param height The height of the node. + ["AlignLayout"]: function(width: number, height: number) end -- A class object for the `Node` class. diff --git a/Assets/Script/Lib/Dora/en/PhysicsWorldType.d.tl b/Assets/Script/Lib/Dora/en/PhysicsWorldType.d.tl index 67f5c7a4a..3d57d8ab2 100644 --- a/Assets/Script/Lib/Dora/en/PhysicsWorldType.d.tl +++ b/Assets/Script/Lib/Dora/en/PhysicsWorldType.d.tl @@ -9,9 +9,6 @@ local record PhysicsWorld -- Inherits from `Node`. embed Node - -- Whether debug graphic should be displayed for the physics world. - showDebug: boolean - -- Queries the physics world for all bodies that intersect with the specified rectangle. -- @param rect (Rect) The rectangle to query for bodies. -- @param handler (function) A function that is called for each body found in the query. diff --git a/Assets/Script/Lib/Dora/en/Spine.d.tl b/Assets/Script/Lib/Dora/en/Spine.d.tl index e7b63037f..9622c8766 100644 --- a/Assets/Script/Lib/Dora/en/Spine.d.tl +++ b/Assets/Script/Lib/Dora/en/Spine.d.tl @@ -6,9 +6,6 @@ local record Spine -- Inherits from `Playable`. embed Playable - -- Whether to show debug graphics. - showDebug: boolean - -- Whether hit testing is enabled. hitTestEnabled: boolean diff --git a/Assets/Script/Lib/Dora/en/dora.d.ts b/Assets/Script/Lib/Dora/en/dora.d.ts index ab7cbffe2..af4f4804e 100644 --- a/Assets/Script/Lib/Dora/en/dora.d.ts +++ b/Assets/Script/Lib/Dora/en/dora.d.ts @@ -2359,6 +2359,7 @@ const enum NodeEvent { ContactStart = "ContactStart", ContactEnd = "ContactEnd", Finished = "Finished", + AlignLayout = "AlignLayout", } export {NodeEvent as Slot}; @@ -2558,6 +2559,13 @@ interface NodeEventHandlerMap { * Triggered after a Particle node started a stop action and then all the active particles end their lives. */ Finished(this: void): void; + + /** + * Triggers when the layout of the `AlignNode` is updated. + * @param width The width of the node. + * @param height The height of the node. + */ + AlignLayout(this: void, width: number, height: number): void; } const enum GlobalEvent { @@ -2750,6 +2758,9 @@ class Node extends Object { /** Whether to group the node's rendering with all its recursive children. */ renderGroup: boolean; + /** Whether to show debug information for the node. */ + showDebug: boolean; + /** The rendering order number for group rendering. Nodes with lower rendering orders are rendered earlier. */ renderOrder: number; @@ -4083,6 +4094,65 @@ interface DrawNodeClass { const drawNodeClass: DrawNodeClass; export {drawNodeClass as DrawNode}; +/** A node used for aligning layout elements. */ +class AlignNode extends Node { + private constructor(); + + /** + * Sets the layout style of the node. + * + * @param style The layout style. + * + * The following properties can be set through a CSS style string: + * + * ## Layout direction and alignment + * * direction: Sets the direction (ltr, rtl, inherit). + * * align-items, align-self, align-content: Sets the alignment of different items (flex-start, center, stretch, flex-end, auto). + * * flex-direction: Sets the layout direction (column, row, column-reverse, row-reverse). + * * justify-content: Sets the arrangement of child items (flex-start, center, flex-end, space-between, space-around, space-evenly). + * + * ## Flex properties + * * flex: Sets the overall size of the flex container. + * * flex-grow: Sets the flex growth value. + * * flex-shrink: Sets the flex shrink value. + * * flex-wrap: Sets whether to wrap (nowrap, wrap, wrap-reverse). + * * flex-basis: Sets the flex basis value or percentage. + * + * ## Margins and dimensions + * * margin: Can be set by a single value or multiple values separated by commas, percentages or auto for each side. + * * margin-top, margin-right, margin-bottom, margin-left, margin-start, margin-end: Sets the margin values, percentages or auto. + * * padding: Can be set by a single value or multiple values separated by commas or percentages for each side. + * * padding-top, padding-right, padding-bottom, padding-left: Sets the padding values or percentages. + * * border: Can be set by a single value or multiple values separated by commas for each side. + * * width, height, min-width, min-height, max-width, max-height: Sets the dimension values or percentage properties. + * + * ## Positioning + * * top, right, bottom, left, start, end, horizontal, vertical: Sets the positioning property values or percentages. + * + * ## Other properties + * * position: Sets the positioning type (absolute, relative, static). + * * overflow: Sets the overflow property (visible, hidden, scroll). + * * display: Controls whether to display (flex, none). + */ + css(style: string): void; +} + +interface AlignNodeClass { + /** + * Creates a new AlignNode object. + * @param isWindowRoot Whether the node is a window root node. A window root node will automatically listen for window size change events and update the layout accordingly. + * @returns The new AlignNode object. + */ + (this: void, isWindowRoot?: boolean): AlignNode; +} + +export namespace AlignNode { + export type Type = AlignNode; +} + +const alignNodeClass: AlignNodeClass; +export {alignNodeClass as AlignNode}; + /** * Emits a global event with the given name and arguments to all listeners registered by `node.gslot()` function. * @param eventName The name of the event to emit. @@ -4170,8 +4240,8 @@ interface EntityClass { (this: void, components: Record): Entity; } -const entity: EntityClass; -export {entity as Entity}; +const entityClass: EntityClass; +export {entityClass as Entity}; /** * A class representing an observer of entity changes in the game systems. diff --git a/Assets/Script/Lib/Dora/en/jsx.d.ts b/Assets/Script/Lib/Dora/en/jsx.d.ts index 2f7d6b0cc..cb23063a0 100644 --- a/Assets/Script/Lib/Dora/en/jsx.d.ts +++ b/Assets/Script/Lib/Dora/en/jsx.d.ts @@ -42,6 +42,9 @@ class Node { /** The Y-axis skew angle of the node in degrees. */ skewY?: number; + /** Whether debug graphic should be displayed for the node. */ + showDebug?: boolean; + /** Whether the node is visible. */ visible?: boolean; @@ -354,11 +357,6 @@ class DragonBone extends Playable { */ file: string; - /** - * Whether to show debug graphics. - */ - showDebug?: boolean; - /** * Whether hit testing is enabled. */ @@ -380,9 +378,6 @@ class Spine extends Playable { */ file: string; - /** Whether to show debug graphics. */ - showDebug?: boolean; - /** Whether hit testing is enabled. */ hitTestEnabled?: boolean; @@ -789,6 +784,90 @@ class Menu extends Node { onMount?(this: void, self: dora.Menu.Type): void; } +type StyleDirection = 'ltr' | 'rtl' | 'inherit'; +type StyleAlign = 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'auto'; +type StyleFlexDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse'; +type StyleJustifyContent = 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly'; +type StylePositionType = 'relative' | 'absolute' | 'static'; +type StyleWrap = 'nowrap' | 'wrap' | 'wrap-reverse'; +type StyleOverflow = 'visible' | 'hidden' | 'scroll'; +type StyleDisplay = 'flex' | 'none'; +type StylePercentage = `${number}%`; +type StyleValuePercent = number | StylePercentage; +type StyleValuePercentAuto = number | StylePercentage | "auto"; + +interface AlignStyle { + direction?: StyleDirection; + alignContent?: StyleAlign; + alignItems?: StyleAlign; + alignSelf?: StyleAlign; + flexDirection?: StyleFlexDirection; + justifyContent?: StyleJustifyContent; + flexWrap?: StyleWrap; + flex?: number; + flexBasis?: StyleValuePercentAuto; + flexGrow?: number; + flexShrink?: number; + left?: StyleValuePercent; + right?: StyleValuePercent; + top?: StyleValuePercent; + bottom?: StyleValuePercent; + start?: StyleValuePercent; + end?: StyleValuePercent; + horizontal?: StyleValuePercent; + vetical?: StyleValuePercent; + position?: StylePositionType; + overflow?: StyleOverflow; + display?: StyleDisplay; + width?: StyleValuePercentAuto; + height?: StyleValuePercentAuto; + minWidth?: StyleValuePercent; + minHeight?: StyleValuePercent; + maxWidth?: StyleValuePercent; + maxHeight?: StyleValuePercent; + marginTop?: StyleValuePercentAuto; + marginRight?: StyleValuePercentAuto; + marginBottom?: StyleValuePercentAuto; + marginLeft?: StyleValuePercentAuto; + marginStart?: StyleValuePercentAuto; + marginEnd?: StyleValuePercentAuto; + margin?: [StyleValuePercentAuto, StyleValuePercentAuto?, StyleValuePercentAuto?, StyleValuePercentAuto?] | StyleValuePercentAuto; + paddingTop?: StyleValuePercent; + paddingRight?: StyleValuePercent; + paddingBottom?: StyleValuePercent; + paddingLeft?: StyleValuePercent; + padding?: [StyleValuePercent, StyleValuePercent?, StyleValuePercent?, StyleValuePercent?] | StyleValuePercent; + border?: [number, number?, number?, number?] | number; + gap?: [StyleValuePercent, StyleValuePercent?] | StyleValuePercent; + aspectRatio?: number; +} + +class AlignNode extends Node { + ref?: Ref; + + /** + * Whether the node is a window root node. + * A window root node will automatically listen for window size change events and update the layout accordingly. + */ + windowRoot?: boolean; + + /** The layout style of the node */ + style?: AlignStyle; + + /** + * Triggers when the layout of the node is updated. + * @param width The width of the node. + * @param height The height of the node. + */ + onLayout?(this: void, width: number, height: number): void; + + /** + * Triggers when this node element is instantialized. + * @param self The node element that was instantialized. + */ + onMount?(this: void, self: dora.AlignNode.Type): void; +} + class Action { ref?: Ref; children: any[] | any; @@ -1029,11 +1108,6 @@ class Sequence { } class PhysicsWorld extends Node { - /** - * Whether debug graphic should be displayed for the physics world. - */ - showDebug?: boolean; - /** * Triggers when this node element is instantialized. * @param self The node element that was instantialized. @@ -1764,6 +1838,10 @@ interface IntrinsicElements { * A class for creating a custom element. */ 'custom-element': CustomElement; + /** + * A class for aligning child nodes within a parent node. + */ + 'align-node': AlignNode; } interface ElementChildrenAttribute { diff --git a/Assets/Script/Lib/Dora/zh-Hans/AlignNode.d.tl b/Assets/Script/Lib/Dora/zh-Hans/AlignNode.d.tl new file mode 100644 index 000000000..a6c800093 --- /dev/null +++ b/Assets/Script/Lib/Dora/zh-Hans/AlignNode.d.tl @@ -0,0 +1,55 @@ +local Node = require("Node").Type + +-- 用于对齐子节点的布局节点。 +local record AlignNode + + -- 继承自“Node”。 + embed Node + + -- 设置节点的布局样式。 + -- @param style (string) 节点的布局样式。 + -- 可通过 CSS 样式字符串设置以下属性: + -- + -- ## 布局方向和对齐 + -- * direction:设置方向(ltr、rtl、inherit)。 + -- * align-items、align-self、align-content:设置不同项目对齐方式(flex-start、center、stretch、flex-end、auto)。 + -- * flex-direction:设定布局方向(column、row、column-reverse、row-reverse)。 + -- * justify-content:设定子项排列方式(flex-start、center、flex-end、space-between、space-around、space-evenly)。 + -- + -- ## Flex 属性 + -- * flex:设定弹性容器的整体大小。 + -- * flex-grow:设定弹性增长值。 + -- * flex-shrink:设定弹性收缩值。 + -- * flex-wrap:设定是否换行(nowrap、wrap、wrap-reverse)。 + -- * flex-basis:设定弹性基础数值或百分比。 + -- + -- ## 边缘和尺寸 + -- * margin:可以通过单一值或逗号分隔的多个数值、百分比或是auto来设定各个边。 + -- * margin-top、margin-right、margin-bottom、margin-left、margin-start、margin-end:设定各个边的数值、百分比或为auto。 + -- * padding:可以通过单一值或逗号分隔的多个数值或是百分比来设定各个边。 + -- * padding-top、padding-right、padding-bottom、padding-left:设定各个边的数值或百分比。 + -- * border:可以通过单一值或逗号分隔的多个数值来设定各个边。 + -- * width、height、min-width、min-height、max-width、max-height:设定尺寸数值或百分比属性。 + -- + -- ## 定位 + -- * top、right、bottom、left、start、end、horizontal、vertical:设定定位属性数值或是百分比。 + -- + -- ## 其他属性 + -- * position:设定定位类型(absolute、relative、static)。 + -- * overflow:设定溢出属性(visible、hidden、scroll)。 + -- * display:控制是否显示(flex、none)。 + css: function(self: AlignNode, style: string) +end + +-- 用于创建 AlignNode 对象的类。 +local record AlignNodeClass + type Type = AlignNode + + -- 创建一个新的 AlignNode 对象。 + -- @param isWindowRoot (boolean) [可选] 是否为窗口根节点。窗口根节点会自动监听窗口大小变化事件自动更新布局。 + -- @return (AlignNode) 新创建的 AlignNode 对象。 + metamethod __call: function(self: AlignNodeClass, isWindowRoot?: boolean): AlignNode +end + +local alignNodeClass: AlignNodeClass +return alignNodeClass diff --git a/Assets/Script/Lib/Dora/zh-Hans/DragonBone.d.tl b/Assets/Script/Lib/Dora/zh-Hans/DragonBone.d.tl index 1eb3cdda0..ca2ad2eea 100644 --- a/Assets/Script/Lib/Dora/zh-Hans/DragonBone.d.tl +++ b/Assets/Script/Lib/Dora/zh-Hans/DragonBone.d.tl @@ -6,9 +6,6 @@ local record DragonBone -- 继承自`Playable`。 embed Playable - -- 是否显示调试图形。 - showDebug: boolean - -- 是否启用碰撞检测的功能。 hitTestEnabled: boolean diff --git a/Assets/Script/Lib/Dora/zh-Hans/Node.d.tl b/Assets/Script/Lib/Dora/zh-Hans/Node.d.tl index 4d479d0ba..961e25b4c 100644 --- a/Assets/Script/Lib/Dora/zh-Hans/Node.d.tl +++ b/Assets/Script/Lib/Dora/zh-Hans/Node.d.tl @@ -142,6 +142,9 @@ local record Node -- 是否将所有层级的子节点按分组编号做分组渲染。 renderGroup: boolean + -- 是否显示节点的调试信息。 + showDebug: boolean + -- 用于分组渲染的分组顺序编号。节点的渲染顺序编号越小,渲染就越靠前。 renderOrder: integer @@ -569,6 +572,11 @@ local record __SLOT__ -- @param controllerId (integer) 控制器ID,当有多个控制器连接时从0开始递增。 -- @param axisValue (number) 控制器轴的值,范围从 -1.0 到 1.0。 ["Axis"]: function(controllerId: integer, axisValue: number) + + -- 当`AlignNode`节点的布局更新时触发。 + -- @param width (number) 节点的宽度。 + -- @param height (number) 节点的高度。 + ["AlignLayout"]: function(width: number, height: number) end -- 用于创建场景节点实例的类。 diff --git a/Assets/Script/Lib/Dora/zh-Hans/PhysicsWorldType.d.tl b/Assets/Script/Lib/Dora/zh-Hans/PhysicsWorldType.d.tl index dd2674852..7da832cca 100644 --- a/Assets/Script/Lib/Dora/zh-Hans/PhysicsWorldType.d.tl +++ b/Assets/Script/Lib/Dora/zh-Hans/PhysicsWorldType.d.tl @@ -9,9 +9,6 @@ local record PhysicsWorld -- 继承自`Node`。 embed Node - -- 是否显示物理世界的调试图形。 - showDebug: boolean - -- 查询与指定矩形区域相交的所有物理体。 -- @param rect (Rect) 要查询的矩形区域。 -- @param handler (function) 对于查询中找到的每个物理体调用的函数。 diff --git a/Assets/Script/Lib/Dora/zh-Hans/Spine.d.tl b/Assets/Script/Lib/Dora/zh-Hans/Spine.d.tl index 1ca4f00a6..b36cb5280 100644 --- a/Assets/Script/Lib/Dora/zh-Hans/Spine.d.tl +++ b/Assets/Script/Lib/Dora/zh-Hans/Spine.d.tl @@ -6,9 +6,6 @@ local record Spine -- 继承自`Playable`. embed Playable - -- 是否显示调试图形。 - showDebug: boolean - -- 是否启用碰撞检测的功能。 hitTestEnabled: boolean diff --git a/Assets/Script/Lib/Dora/zh-Hans/dora.d.ts b/Assets/Script/Lib/Dora/zh-Hans/dora.d.ts index 586520e6c..4ceef0810 100644 --- a/Assets/Script/Lib/Dora/zh-Hans/dora.d.ts +++ b/Assets/Script/Lib/Dora/zh-Hans/dora.d.ts @@ -2360,6 +2360,7 @@ const enum NodeEvent { ContactStart = "ContactStart", ContactEnd = "ContactEnd", Finished = "Finished", + AlignLayout = "AlignLayout", } export {NodeEvent as Slot}; @@ -2559,6 +2560,13 @@ interface NodeEventHandlerMap { * 当粒子系统节点在启动之后又停止发射粒子,并等待所有已发射的粒子结束它们的生命周期时触发。 */ Finished(this: void): void; + + /** + * 当`AlignNode`的布局更新时触发。 + * @param width 节点的宽度。 + * @param height 节点的高度。 + */ + AlignLayout(this: void, width: number, height: number): void; } const enum GlobalEvent { @@ -2753,6 +2761,9 @@ class Node extends Object { /** 是否将节点的渲染与其所有递归子项分组。 */ renderGroup: boolean; + /** 是否显示节点的调试信息。 */ + showDebug: boolean; + /** 组渲染的渲染顺序号。渲染顺序较低的节点将更早渲染。 */ renderOrder: number; @@ -4087,6 +4098,66 @@ interface DrawNodeClass { const drawNodeClass: DrawNodeClass; export {drawNodeClass as DrawNode}; +/** 用于对齐子节点的布局节点。 */ +class AlignNode extends Node { + private constructor(); + + /** + * 设置节点的布局样式。 + * + * @param style 节点的布局样式。 + * + * 可通过 CSS 样式字符串设置以下属性: + * + * ## 布局方向和对齐 + * * direction:设置方向(ltr、rtl、inherit)。 + * * align-items、align-self、align-content:设置不同项目对齐方式(flex-start、center、stretch、flex-end、auto)。 + * * flex-direction:设定布局方向(column、row、column-reverse、row-reverse)。 + * * justify-content:设定子项排列方式(flex-start、center、flex-end、space-between、space-around、space-evenly)。 + * + * ## Flex 属性 + * * flex:设定弹性容器的整体大小。 + * * flex-grow:设定弹性增长值。 + * * flex-shrink:设定弹性收缩值。 + * * flex-wrap:设定是否换行(nowrap、wrap、wrap-reverse)。 + * * flex-basis:设定弹性基础数值或百分比。 + * + * ## 边缘和尺寸 + * * margin:可以通过单一值或逗号分隔的多个数值、百分比或是auto来设定各个边。 + * * margin-top、margin-right、margin-bottom、margin-left、margin-start、margin-end:设定各个边的数值、百分比或为auto。 + * * padding:可以通过单一值或逗号分隔的多个数值或是百分比来设定各个边。 + * * padding-top、padding-right、padding-bottom、padding-left:设定各个边的数值或百分比。 + * * border:可以通过单一值或逗号分隔的多个数值来设定各个边。 + * * width、height、min-width、min-height、max-width、max-height:设定尺寸数值或百分比属性。 + * + * ## 定位 + * * top、right、bottom、left、start、end、horizontal、vertical:设定定位属性数值或是百分比。 + * + * ## 其他属性 + * * position:设定定位类型(absolute、relative、static)。 + * * overflow:设定溢出属性(visible、hidden、scroll)。 + * * display:控制是否显示(flex、none)。 + */ + css(style: string): void; +} + +interface AlignNodeClass { + /** + * 创建一个新的 AlignNode 对象。 + * + * @param isWindowRoot 是否为窗口根节点。窗口根节点会自动监听窗口大小变化事件自动更新布局。 + * @returns 新创建的 AlignNode 对象。 + */ + (this: void, isWindowRoot?: boolean): AlignNode; +} + +export namespace AlignNode { + export type Type = AlignNode; +} + +const alignNodeClass: AlignNodeClass; +export {alignNodeClass as AlignNode}; + /** * 发送具有特定名称和参数的全局事件,传递给所有由`node.gslot()`函数注册的事件监听器。 * @param eventName 要发出的事件的名称。 diff --git a/Assets/Script/Lib/Dora/zh-Hans/jsx.d.ts b/Assets/Script/Lib/Dora/zh-Hans/jsx.d.ts index 039f2585c..ad96e5595 100644 --- a/Assets/Script/Lib/Dora/zh-Hans/jsx.d.ts +++ b/Assets/Script/Lib/Dora/zh-Hans/jsx.d.ts @@ -42,6 +42,9 @@ class Node { /** 节点的Y轴倾斜角度,单位为度。 */ skewY?: number; + /** 是否显示调试图形。 */ + showDebug?: boolean; + /** 节点是否可见。 */ visible?: boolean; @@ -354,11 +357,6 @@ class DragonBone extends Playable { */ file: string; - /** - * 是否显示调试图形。 - */ - showDebug?: boolean; - /** * 是否启用命中测试。 */ @@ -380,9 +378,6 @@ class Spine extends Playable { */ file: string; - /** 是否显示调试图形。 */ - showDebug?: boolean; - /** 是否启用命中测试。 */ hitTestEnabled?: boolean; @@ -789,6 +784,87 @@ class Menu extends Node { onMount?(this: void, self: dora.Menu.Type): void; } +type StyleDirection = 'ltr' | 'rtl' | 'inherit'; +type StyleAlign = 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'auto'; +type StyleFlexDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse'; +type StyleJustifyContent = 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly'; +type StylePositionType = 'relative' | 'absolute' | 'static'; +type StyleWrap = 'nowrap' | 'wrap' | 'wrap-reverse'; +type StyleOverflow = 'visible' | 'hidden' | 'scroll'; +type StyleDisplay = 'flex' | 'none'; +type StylePercentage = `${number}%`; +type StyleValuePercent = number | StylePercentage; +type StyleValuePercentAuto = number | StylePercentage | "auto"; + +interface AlignStyle { + direction?: StyleDirection; + alignContent?: StyleAlign; + alignItems?: StyleAlign; + alignSelf?: StyleAlign; + flexDirection?: StyleFlexDirection; + justifyContent?: StyleJustifyContent; + flexWrap?: StyleWrap; + flex?: number; + flexBasis?: StyleValuePercentAuto; + flexGrow?: number; + flexShrink?: number; + left?: StyleValuePercent; + right?: StyleValuePercent; + top?: StyleValuePercent; + bottom?: StyleValuePercent; + start?: StyleValuePercent; + end?: StyleValuePercent; + horizontal?: StyleValuePercent; + vetical?: StyleValuePercent; + position?: StylePositionType; + overflow?: StyleOverflow; + display?: StyleDisplay; + width?: StyleValuePercentAuto; + height?: StyleValuePercentAuto; + minWidth?: StyleValuePercent; + minHeight?: StyleValuePercent; + maxWidth?: StyleValuePercent; + maxHeight?: StyleValuePercent; + marginTop?: StyleValuePercentAuto; + marginRight?: StyleValuePercentAuto; + marginBottom?: StyleValuePercentAuto; + marginLeft?: StyleValuePercentAuto; + marginStart?: StyleValuePercentAuto; + marginEnd?: StyleValuePercentAuto; + margin?: [StyleValuePercentAuto, StyleValuePercentAuto?, StyleValuePercentAuto?, StyleValuePercentAuto?] | StyleValuePercentAuto; + paddingTop?: StyleValuePercent; + paddingRight?: StyleValuePercent; + paddingBottom?: StyleValuePercent; + paddingLeft?: StyleValuePercent; + padding?: [StyleValuePercent, StyleValuePercent?, StyleValuePercent?, StyleValuePercent?] | StyleValuePercent; + border?: [number, number?, number?, number?] | number; + gap?: [StyleValuePercent, StyleValuePercent?] | StyleValuePercent; + aspectRatio?: number; +} + +class AlignNode extends Node { + ref?: Ref; + + /** 是否为窗口根节点。窗口根节点会自动监听窗口大小变化事件自动更新布局。 */ + windowRoot?: boolean; + + /** 节点的布局样式 */ + style?: AlignStyle; + + /** + * 当节点的布局样式发生变化时,会触发该回调函数。 + * @param width 当前节点的宽度。 + * @param height 当前节点的高度。 + */ + onLayout?(this: void, width: number, height: number): void; + + /** + * 当前节点被实例化时,会触发该回调函数。 + * @param self 当前节点的实例。 + */ + onMount?(this: void, self: dora.AlignNode.Type): void; +} + class Action { ref?: Ref; children: any[] | any; @@ -1031,11 +1107,6 @@ class Sequence { } class PhysicsWorld extends Node { - /** - * 是否应为物理世界显示调试图形。 - */ - showDebug?: boolean; - /** * 当前节点被实例化时,会触发该回调函数。 * @param self 当前节点的实例。 @@ -1761,6 +1832,10 @@ interface IntrinsicElements { * 创建一个自定义的标签。 */ 'custom-element': CustomElement; + /** + * 用于对齐子节点的布局节点。 + */ + 'align-node': AlignNode; } interface ElementChildrenAttribute { diff --git a/Assets/Script/Lib/WebServer.lua b/Assets/Script/Lib/WebServer.lua index eae1b6920..7e5b5947e 100644 --- a/Assets/Script/Lib/WebServer.lua +++ b/Assets/Script/Lib/WebServer.lua @@ -1205,7 +1205,7 @@ end, function(req, file) -- 497 end -- 505 end -- 505 if path ~= nil then -- 498 - path = Content:getFullPath(path) -- 499 + path = Path(Content.writablePath, path) -- 499 if Content:exist(path) then -- 500 local uploadPath = Path(Content.writablePath, ".upload") -- 501 local targetPath = Path(path, Path:getRelative(file, uploadPath)) -- 502 @@ -1780,255 +1780,261 @@ HttpServer:postSchedule("/run", function(req) -- 736 local _type_1 = type(_obj_0) -- 737 if "table" == _type_1 or "userdata" == _type_1 then -- 737 file = _obj_0.file -- 737 - end -- 749 - end -- 749 + end -- 752 + end -- 752 local asProj -- 737 do -- 737 local _obj_0 = req.body -- 737 local _type_1 = type(_obj_0) -- 737 if "table" == _type_1 or "userdata" == _type_1 then -- 737 asProj = _obj_0.asProj -- 737 - end -- 749 - end -- 749 + end -- 752 + end -- 752 if file ~= nil and asProj ~= nil then -- 737 - local Entry = require("Script.Dev.Entry") -- 738 - if asProj then -- 739 - local proj = getProjectDirFromFile(file) -- 740 - if proj then -- 740 - Entry.allClear() -- 741 - local target = Path(proj, "init") -- 742 - local success, err = Entry.enterEntryAsync({ -- 743 - "Project", -- 743 - target -- 743 - }) -- 743 - target = Path:getName(Path:getPath(target)) -- 744 - return { -- 745 - success = success, -- 745 - target = target, -- 745 - err = err -- 745 - } -- 745 + if not Content:exist(file) then -- 738 + local devFile = Path(Content.writablePath, file) -- 739 + if Content:exist(devFile) then -- 740 + file = devFile -- 740 end -- 740 - end -- 739 - Entry.allClear() -- 746 - file = Path:replaceExt(file, "") -- 747 - local success, err = Entry.enterEntryAsync({ -- 748 - Path:getName(file), -- 748 - file -- 748 - }) -- 748 - return { -- 749 - success = success, -- 749 - err = err -- 749 - } -- 749 + end -- 738 + local Entry = require("Script.Dev.Entry") -- 741 + if asProj then -- 742 + local proj = getProjectDirFromFile(file) -- 743 + if proj then -- 743 + Entry.allClear() -- 744 + local target = Path(proj, "init") -- 745 + local success, err = Entry.enterEntryAsync({ -- 746 + "Project", -- 746 + target -- 746 + }) -- 746 + target = Path:getName(Path:getPath(target)) -- 747 + return { -- 748 + success = success, -- 748 + target = target, -- 748 + err = err -- 748 + } -- 748 + end -- 743 + end -- 742 + Entry.allClear() -- 749 + file = Path:replaceExt(file, "") -- 750 + local success, err = Entry.enterEntryAsync({ -- 751 + Path:getName(file), -- 751 + file -- 751 + }) -- 751 + return { -- 752 + success = success, -- 752 + err = err -- 752 + } -- 752 end -- 737 - end -- 749 - end -- 749 + end -- 752 + end -- 752 return { -- 736 success = false -- 736 - } -- 749 + } -- 752 end) -- 736 -HttpServer:postSchedule("/stop", function() -- 751 - local Entry = require("Script.Dev.Entry") -- 752 - return { -- 753 - success = Entry.stop() -- 753 - } -- 753 -end) -- 751 -HttpServer:postSchedule("/zip", function(req) -- 755 - do -- 756 - local _type_0 = type(req) -- 756 - local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 756 - if _tab_0 then -- 756 - local path -- 756 - do -- 756 - local _obj_0 = req.body -- 756 - local _type_1 = type(_obj_0) -- 756 - if "table" == _type_1 or "userdata" == _type_1 then -- 756 - path = _obj_0.path -- 756 - end -- 759 - end -- 759 - local zipFile -- 756 - do -- 756 - local _obj_0 = req.body -- 756 - local _type_1 = type(_obj_0) -- 756 - if "table" == _type_1 or "userdata" == _type_1 then -- 756 - zipFile = _obj_0.zipFile -- 756 - end -- 759 - end -- 759 - if path ~= nil and zipFile ~= nil then -- 756 - Content:mkdir(Path:getPath(zipFile)) -- 757 - return { -- 758 - success = Content:zipAsync(path, zipFile, function(file) -- 758 - return not (file:match('^%.') or file:match("[\\/]%.")) -- 759 - end) -- 758 - } -- 759 - end -- 756 - end -- 759 - end -- 759 - return { -- 755 - success = false -- 755 - } -- 759 -end) -- 755 -HttpServer:postSchedule("/unzip", function(req) -- 761 - do -- 762 - local _type_0 = type(req) -- 762 - local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 762 - if _tab_0 then -- 762 - local zipFile -- 762 - do -- 762 - local _obj_0 = req.body -- 762 - local _type_1 = type(_obj_0) -- 762 - if "table" == _type_1 or "userdata" == _type_1 then -- 762 - zipFile = _obj_0.zipFile -- 762 - end -- 764 - end -- 764 - local path -- 762 - do -- 762 - local _obj_0 = req.body -- 762 - local _type_1 = type(_obj_0) -- 762 - if "table" == _type_1 or "userdata" == _type_1 then -- 762 - path = _obj_0.path -- 762 - end -- 764 - end -- 764 - if zipFile ~= nil and path ~= nil then -- 762 - return { -- 763 - success = Content:unzipAsync(zipFile, path, function(file) -- 763 - return not (file:match('^%.') or file:match("[\\/]%.") or file:match("__MACOSX")) -- 764 - end) -- 763 - } -- 764 +HttpServer:postSchedule("/stop", function() -- 754 + local Entry = require("Script.Dev.Entry") -- 755 + return { -- 756 + success = Entry.stop() -- 756 + } -- 756 +end) -- 754 +HttpServer:postSchedule("/zip", function(req) -- 758 + do -- 759 + local _type_0 = type(req) -- 759 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 759 + if _tab_0 then -- 759 + local path -- 759 + do -- 759 + local _obj_0 = req.body -- 759 + local _type_1 = type(_obj_0) -- 759 + if "table" == _type_1 or "userdata" == _type_1 then -- 759 + path = _obj_0.path -- 759 + end -- 762 end -- 762 - end -- 764 - end -- 764 - return { -- 761 - success = false -- 761 - } -- 764 -end) -- 761 -HttpServer:post("/editingInfo", function(req) -- 766 - local Entry = require("Script.Dev.Entry") -- 767 - local config = Entry.getConfig() -- 768 - local _type_0 = type(req) -- 769 - local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 769 - local _match_0 = false -- 769 - if _tab_0 then -- 769 - local editingInfo -- 769 - do -- 769 - local _obj_0 = req.body -- 769 - local _type_1 = type(_obj_0) -- 769 - if "table" == _type_1 or "userdata" == _type_1 then -- 769 - editingInfo = _obj_0.editingInfo -- 769 - end -- 771 - end -- 771 - if editingInfo ~= nil then -- 769 - _match_0 = true -- 769 - config.editingInfo = editingInfo -- 770 - return { -- 771 - success = true -- 771 - } -- 771 - end -- 769 - end -- 769 - if not _match_0 then -- 769 - if not (config.editingInfo ~= nil) then -- 773 - local json = require("json") -- 774 - local folder -- 775 - if App.locale:match('^zh') then -- 775 - folder = 'zh-Hans' -- 775 - else -- 775 - folder = 'en' -- 775 - end -- 775 - config.editingInfo = json.dump({ -- 777 - index = 0, -- 777 - files = { -- 779 - { -- 780 - key = Path(Content.assetPath, 'Doc', folder, 'welcome.md'), -- 780 - title = "welcome.md" -- 781 - } -- 779 - } -- 778 - }) -- 776 - end -- 773 - return { -- 785 - success = true, -- 785 - editingInfo = config.editingInfo -- 785 - } -- 785 - end -- 785 -end) -- 766 -HttpServer:post("/command", function(req) -- 787 - do -- 788 - local _type_0 = type(req) -- 788 - local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 788 - if _tab_0 then -- 788 - local code -- 788 - do -- 788 - local _obj_0 = req.body -- 788 - local _type_1 = type(_obj_0) -- 788 - if "table" == _type_1 or "userdata" == _type_1 then -- 788 - code = _obj_0.code -- 788 - end -- 790 - end -- 790 - if code ~= nil then -- 788 - emit("AppCommand", code) -- 789 - return { -- 790 - success = true -- 790 - } -- 790 - end -- 788 - end -- 790 - end -- 790 - return { -- 787 - success = false -- 787 - } -- 790 -end) -- 787 -HttpServer:post("/exist", function(req) -- 792 - do -- 793 - local _type_0 = type(req) -- 793 - local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 793 - if _tab_0 then -- 793 - local file -- 793 - do -- 793 - local _obj_0 = req.body -- 793 - local _type_1 = type(_obj_0) -- 793 - if "table" == _type_1 or "userdata" == _type_1 then -- 793 - file = _obj_0.file -- 793 - end -- 794 - end -- 794 - if file ~= nil then -- 793 - return { -- 794 - success = Content:exist(file) -- 794 - } -- 794 + local zipFile -- 759 + do -- 759 + local _obj_0 = req.body -- 759 + local _type_1 = type(_obj_0) -- 759 + if "table" == _type_1 or "userdata" == _type_1 then -- 759 + zipFile = _obj_0.zipFile -- 759 + end -- 762 + end -- 762 + if path ~= nil and zipFile ~= nil then -- 759 + Content:mkdir(Path:getPath(zipFile)) -- 760 + return { -- 761 + success = Content:zipAsync(path, zipFile, function(file) -- 761 + return not (file:match('^%.') or file:match("[\\/]%.")) -- 762 + end) -- 761 + } -- 762 + end -- 759 + end -- 762 + end -- 762 + return { -- 758 + success = false -- 758 + } -- 762 +end) -- 758 +HttpServer:postSchedule("/unzip", function(req) -- 764 + do -- 765 + local _type_0 = type(req) -- 765 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 765 + if _tab_0 then -- 765 + local zipFile -- 765 + do -- 765 + local _obj_0 = req.body -- 765 + local _type_1 = type(_obj_0) -- 765 + if "table" == _type_1 or "userdata" == _type_1 then -- 765 + zipFile = _obj_0.zipFile -- 765 + end -- 767 + end -- 767 + local path -- 765 + do -- 765 + local _obj_0 = req.body -- 765 + local _type_1 = type(_obj_0) -- 765 + if "table" == _type_1 or "userdata" == _type_1 then -- 765 + path = _obj_0.path -- 765 + end -- 767 + end -- 767 + if zipFile ~= nil and path ~= nil then -- 765 + return { -- 766 + success = Content:unzipAsync(zipFile, path, function(file) -- 766 + return not (file:match('^%.') or file:match("[\\/]%.") or file:match("__MACOSX")) -- 767 + end) -- 766 + } -- 767 + end -- 765 + end -- 767 + end -- 767 + return { -- 764 + success = false -- 764 + } -- 767 +end) -- 764 +HttpServer:post("/editingInfo", function(req) -- 769 + local Entry = require("Script.Dev.Entry") -- 770 + local config = Entry.getConfig() -- 771 + local _type_0 = type(req) -- 772 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 772 + local _match_0 = false -- 772 + if _tab_0 then -- 772 + local editingInfo -- 772 + do -- 772 + local _obj_0 = req.body -- 772 + local _type_1 = type(_obj_0) -- 772 + if "table" == _type_1 or "userdata" == _type_1 then -- 772 + editingInfo = _obj_0.editingInfo -- 772 + end -- 774 + end -- 774 + if editingInfo ~= nil then -- 772 + _match_0 = true -- 772 + config.editingInfo = editingInfo -- 773 + return { -- 774 + success = true -- 774 + } -- 774 + end -- 772 + end -- 772 + if not _match_0 then -- 772 + if not (config.editingInfo ~= nil) then -- 776 + local json = require("json") -- 777 + local folder -- 778 + if App.locale:match('^zh') then -- 778 + folder = 'zh-Hans' -- 778 + else -- 778 + folder = 'en' -- 778 + end -- 778 + config.editingInfo = json.dump({ -- 780 + index = 0, -- 780 + files = { -- 782 + { -- 783 + key = Path(Content.assetPath, 'Doc', folder, 'welcome.md'), -- 783 + title = "welcome.md" -- 784 + } -- 782 + } -- 781 + }) -- 779 + end -- 776 + return { -- 788 + success = true, -- 788 + editingInfo = config.editingInfo -- 788 + } -- 788 + end -- 788 +end) -- 769 +HttpServer:post("/command", function(req) -- 790 + do -- 791 + local _type_0 = type(req) -- 791 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 791 + if _tab_0 then -- 791 + local code -- 791 + do -- 791 + local _obj_0 = req.body -- 791 + local _type_1 = type(_obj_0) -- 791 + if "table" == _type_1 or "userdata" == _type_1 then -- 791 + code = _obj_0.code -- 791 + end -- 793 end -- 793 - end -- 794 - end -- 794 - return { -- 792 - success = false -- 792 - } -- 794 -end) -- 792 -local status = { -- 796 - url = nil -- 796 -} -- 796 -_module_0 = status -- 797 -thread(function() -- 799 - local doraWeb = Path(Content.assetPath, "www", "index.html") -- 800 - local doraReady = Path(Content.writablePath, ".www", "dora-ready") -- 801 - if Content:exist(doraWeb) then -- 802 - local needReload -- 803 - if Content:exist(doraReady) then -- 803 - needReload = App.version ~= Content:load(doraReady) -- 804 - else -- 805 - needReload = true -- 805 - end -- 803 - if needReload then -- 806 - Content:remove(Path(Content.writablePath, ".www")) -- 807 - Content:copyAsync(Path(Content.assetPath, "www"), Path(Content.writablePath, ".www")) -- 808 - Content:save(doraReady, App.version) -- 812 - print("Dora Dora is ready!") -- 813 + if code ~= nil then -- 791 + emit("AppCommand", code) -- 792 + return { -- 793 + success = true -- 793 + } -- 793 + end -- 791 + end -- 793 + end -- 793 + return { -- 790 + success = false -- 790 + } -- 793 +end) -- 790 +HttpServer:post("/exist", function(req) -- 795 + do -- 796 + local _type_0 = type(req) -- 796 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 796 + if _tab_0 then -- 796 + local file -- 796 + do -- 796 + local _obj_0 = req.body -- 796 + local _type_1 = type(_obj_0) -- 796 + if "table" == _type_1 or "userdata" == _type_1 then -- 796 + file = _obj_0.file -- 796 + end -- 797 + end -- 797 + if file ~= nil then -- 796 + return { -- 797 + success = Content:exist(file) -- 797 + } -- 797 + end -- 796 + end -- 797 + end -- 797 + return { -- 795 + success = false -- 795 + } -- 797 +end) -- 795 +local status = { -- 799 + url = nil -- 799 +} -- 799 +_module_0 = status -- 800 +thread(function() -- 802 + local doraWeb = Path(Content.assetPath, "www", "index.html") -- 803 + local doraReady = Path(Content.writablePath, ".www", "dora-ready") -- 804 + if Content:exist(doraWeb) then -- 805 + local needReload -- 806 + if Content:exist(doraReady) then -- 806 + needReload = App.version ~= Content:load(doraReady) -- 807 + else -- 808 + needReload = true -- 808 end -- 806 - end -- 802 - if HttpServer:start(8866) then -- 814 - local localIP = HttpServer.localIP -- 815 - if localIP == "" then -- 816 - localIP = "localhost" -- 816 - end -- 816 - status.url = "http://" .. tostring(localIP) .. ":8866" -- 817 - return HttpServer:startWS(8868) -- 818 - else -- 820 - status.url = nil -- 820 - return print("8866 Port not available!") -- 821 - end -- 814 -end) -- 799 -return _module_0 -- 821 + if needReload then -- 809 + Content:remove(Path(Content.writablePath, ".www")) -- 810 + Content:copyAsync(Path(Content.assetPath, "www"), Path(Content.writablePath, ".www")) -- 811 + Content:save(doraReady, App.version) -- 815 + print("Dora Dora is ready!") -- 816 + end -- 809 + end -- 805 + if HttpServer:start(8866) then -- 817 + local localIP = HttpServer.localIP -- 818 + if localIP == "" then -- 819 + localIP = "localhost" -- 819 + end -- 819 + status.url = "http://" .. tostring(localIP) .. ":8866" -- 820 + return HttpServer:startWS(8868) -- 821 + else -- 823 + status.url = nil -- 823 + return print("8866 Port not available!") -- 824 + end -- 817 +end) -- 802 +return _module_0 -- 824 diff --git a/Assets/Script/Lib/WebServer.yue b/Assets/Script/Lib/WebServer.yue index 915d333f1..20daf45c3 100644 --- a/Assets/Script/Lib/WebServer.yue +++ b/Assets/Script/Lib/WebServer.yue @@ -496,7 +496,7 @@ HttpServer\upload( return targetPath (req, file): false -> switch req when {params: {:path}} - path = Content\getFullPath path + path = Path Content.writablePath, path if Content\exist path uploadPath = Path Content.writablePath, ".upload" targetPath = Path path, Path\getRelative file, uploadPath @@ -735,6 +735,9 @@ HttpServer\post "/assets", -> HttpServer\postSchedule "/run", (req): success: false -> switch req when {body: {:file, :asProj}} + if not Content\exist file + devFile = Path Content.writablePath, file + file = devFile if Content\exist devFile import "Script.Dev.Entry" if asProj if proj := getProjectDirFromFile file diff --git a/Assets/Script/Lib/dora-x.lua b/Assets/Script/Lib/dora-x.lua index 05f9155d0..76e30fb97 100644 --- a/Assets/Script/Lib/dora-x.lua +++ b/Assets/Script/Lib/dora-x.lua @@ -13,48 +13,48 @@ local dora = require("dora") -- 10 function Warn(msg) -- 12 print("[Dora Warning] " .. msg) -- 13 end -- 13 -function visitNode(nodeStack, node, parent) -- 1249 - if type(node) ~= "table" then -- 1249 - return -- 1251 - end -- 1251 - local enode = node -- 1253 - if enode.type == nil then -- 1253 - local list = node -- 1255 - if #list > 0 then -- 1255 - for i = 1, #list do -- 1255 - local stack = {} -- 1258 - visitNode(stack, list[i], parent) -- 1259 - for i = 1, #stack do -- 1259 - nodeStack[#nodeStack + 1] = stack[i] -- 1261 - end -- 1261 - end -- 1261 - end -- 1261 - else -- 1261 - local handler = elementMap[enode.type] -- 1266 - if handler ~= nil then -- 1266 - handler(nodeStack, enode, parent) -- 1268 - else -- 1268 - Warn(("unsupported tag <" .. enode.type) .. ">") -- 1270 - end -- 1270 - end -- 1270 -end -- 1270 -function ____exports.toNode(enode) -- 1275 - local nodeStack = {} -- 1276 - visitNode(nodeStack, enode) -- 1277 - if #nodeStack == 1 then -- 1277 - return nodeStack[1] -- 1279 - elseif #nodeStack > 1 then -- 1279 - local node = dora.Node() -- 1281 - for i = 1, #nodeStack do -- 1281 - node:addChild(nodeStack[i]) -- 1283 - end -- 1283 - return node -- 1285 - end -- 1285 - return nil -- 1287 -end -- 1275 -____exports.React = {} -- 1275 -local React = ____exports.React -- 1275 -do -- 1275 +function visitNode(nodeStack, node, parent) -- 1286 + if type(node) ~= "table" then -- 1286 + return -- 1288 + end -- 1288 + local enode = node -- 1290 + if enode.type == nil then -- 1290 + local list = node -- 1292 + if #list > 0 then -- 1292 + for i = 1, #list do -- 1292 + local stack = {} -- 1295 + visitNode(stack, list[i], parent) -- 1296 + for i = 1, #stack do -- 1296 + nodeStack[#nodeStack + 1] = stack[i] -- 1298 + end -- 1298 + end -- 1298 + end -- 1298 + else -- 1298 + local handler = elementMap[enode.type] -- 1303 + if handler ~= nil then -- 1303 + handler(nodeStack, enode, parent) -- 1305 + else -- 1305 + Warn(("unsupported tag <" .. enode.type) .. ">") -- 1307 + end -- 1307 + end -- 1307 +end -- 1307 +function ____exports.toNode(enode) -- 1312 + local nodeStack = {} -- 1313 + visitNode(nodeStack, enode) -- 1314 + if #nodeStack == 1 then -- 1314 + return nodeStack[1] -- 1316 + elseif #nodeStack > 1 then -- 1316 + local node = dora.Node() -- 1318 + for i = 1, #nodeStack do -- 1318 + node:addChild(nodeStack[i]) -- 1320 + end -- 1320 + return node -- 1322 + end -- 1322 + return nil -- 1324 +end -- 1312 +____exports.React = {} -- 1312 +local React = ____exports.React -- 1312 +do -- 1312 React.Component = __TS__Class() -- 16 local Component = React.Component -- 16 Component.name = "Component" -- 18 @@ -410,1494 +410,1541 @@ do -- 222 local function handleDragonBoneAttribute(cnode, enode, k, v) -- 242 repeat -- 242 local ____switch52 = k -- 242 - local ____cond52 = ____switch52 == "showDebug" -- 242 + local ____cond52 = ____switch52 == "hitTestEnabled" -- 242 if ____cond52 then -- 242 - cnode.showDebug = v -- 244 + cnode.hitTestEnabled = true -- 244 return true -- 244 end -- 244 - ____cond52 = ____cond52 or ____switch52 == "hitTestEnabled" -- 244 - if ____cond52 then -- 244 - cnode.hitTestEnabled = true -- 245 - return true -- 245 - end -- 245 - until true -- 245 - return handlePlayableAttribute(cnode, enode, k, v) -- 247 + until true -- 244 + return handlePlayableAttribute(cnode, enode, k, v) -- 246 end -- 242 - getDragonBone = function(enode) -- 249 - local node = dora.DragonBone(enode.props.file) -- 250 - if node ~= nil then -- 250 - local cnode = getPlayable(enode, node, handleDragonBoneAttribute) -- 252 - return cnode -- 253 - end -- 253 - return nil -- 255 - end -- 249 - local function handleSpineAttribute(cnode, enode, k, v) -- 258 - repeat -- 258 - local ____switch56 = k -- 258 - local ____cond56 = ____switch56 == "showDebug" -- 258 - if ____cond56 then -- 258 - cnode.showDebug = v -- 260 - return true -- 260 - end -- 260 - ____cond56 = ____cond56 or ____switch56 == "hitTestEnabled" -- 260 - if ____cond56 then -- 260 - cnode.hitTestEnabled = true -- 261 - return true -- 261 - end -- 261 - until true -- 261 - return handlePlayableAttribute(cnode, enode, k, v) -- 263 - end -- 258 - getSpine = function(enode) -- 265 - local node = dora.Spine(enode.props.file) -- 266 - if node ~= nil then -- 266 - local cnode = getPlayable(enode, node, handleSpineAttribute) -- 268 - return cnode -- 269 - end -- 269 - return nil -- 271 - end -- 265 - local function handleModelAttribute(cnode, enode, k, v) -- 274 - repeat -- 274 - local ____switch60 = k -- 274 - local ____cond60 = ____switch60 == "reversed" -- 274 - if ____cond60 then -- 274 - cnode.reversed = v -- 276 - return true -- 276 - end -- 276 - until true -- 276 - return handlePlayableAttribute(cnode, enode, k, v) -- 278 - end -- 274 - getModel = function(enode) -- 280 - local node = dora.Model(enode.props.file) -- 281 - if node ~= nil then -- 281 - local cnode = getPlayable(enode, node, handleModelAttribute) -- 283 - return cnode -- 284 - end -- 284 - return nil -- 286 - end -- 280 -end -- 280 -local getDrawNode -- 290 -do -- 290 - local function handleDrawNodeAttribute(cnode, _enode, k, v) -- 292 - repeat -- 292 - local ____switch65 = k -- 292 - local ____cond65 = ____switch65 == "depthWrite" -- 292 + getDragonBone = function(enode) -- 248 + local node = dora.DragonBone(enode.props.file) -- 249 + if node ~= nil then -- 249 + local cnode = getPlayable(enode, node, handleDragonBoneAttribute) -- 251 + return cnode -- 252 + end -- 252 + return nil -- 254 + end -- 248 + local function handleSpineAttribute(cnode, enode, k, v) -- 257 + repeat -- 257 + local ____switch56 = k -- 257 + local ____cond56 = ____switch56 == "hitTestEnabled" -- 257 + if ____cond56 then -- 257 + cnode.hitTestEnabled = true -- 259 + return true -- 259 + end -- 259 + until true -- 259 + return handlePlayableAttribute(cnode, enode, k, v) -- 261 + end -- 257 + getSpine = function(enode) -- 263 + local node = dora.Spine(enode.props.file) -- 264 + if node ~= nil then -- 264 + local cnode = getPlayable(enode, node, handleSpineAttribute) -- 266 + return cnode -- 267 + end -- 267 + return nil -- 269 + end -- 263 + local function handleModelAttribute(cnode, enode, k, v) -- 272 + repeat -- 272 + local ____switch60 = k -- 272 + local ____cond60 = ____switch60 == "reversed" -- 272 + if ____cond60 then -- 272 + cnode.reversed = v -- 274 + return true -- 274 + end -- 274 + until true -- 274 + return handlePlayableAttribute(cnode, enode, k, v) -- 276 + end -- 272 + getModel = function(enode) -- 278 + local node = dora.Model(enode.props.file) -- 279 + if node ~= nil then -- 279 + local cnode = getPlayable(enode, node, handleModelAttribute) -- 281 + return cnode -- 282 + end -- 282 + return nil -- 284 + end -- 278 +end -- 278 +local getDrawNode -- 288 +do -- 288 + local function handleDrawNodeAttribute(cnode, _enode, k, v) -- 290 + repeat -- 290 + local ____switch65 = k -- 290 + local ____cond65 = ____switch65 == "depthWrite" -- 290 + if ____cond65 then -- 290 + cnode.depthWrite = v -- 292 + return true -- 292 + end -- 292 + ____cond65 = ____cond65 or ____switch65 == "blendFunc" -- 292 if ____cond65 then -- 292 - cnode.depthWrite = v -- 294 - return true -- 294 - end -- 294 - ____cond65 = ____cond65 or ____switch65 == "blendFunc" -- 294 - if ____cond65 then -- 294 - cnode.blendFunc = v -- 295 - return true -- 295 - end -- 295 - until true -- 295 - return false -- 297 - end -- 292 - getDrawNode = function(enode) -- 299 - local node = dora.DrawNode() -- 300 - local cnode = getNode(enode, node, handleDrawNodeAttribute) -- 301 - local ____enode_7 = enode -- 302 - local children = ____enode_7.children -- 302 - for i = 1, #children do -- 302 - do -- 302 - local child = children[i] -- 304 - if type(child) ~= "table" then -- 304 - goto __continue67 -- 306 - end -- 306 - repeat -- 306 - local ____switch69 = child.type -- 306 - local ____cond69 = ____switch69 == "dot-shape" -- 306 - if ____cond69 then -- 306 - do -- 306 - local dot = child.props -- 310 - node:drawDot( -- 311 - dora.Vec2(dot.x or 0, dot.y or 0), -- 312 - dot.radius, -- 313 - dora.Color(dot.color or 4294967295) -- 314 - ) -- 314 - break -- 316 - end -- 316 - end -- 316 - ____cond69 = ____cond69 or ____switch69 == "segment-shape" -- 316 - if ____cond69 then -- 316 - do -- 316 - local segment = child.props -- 319 - node:drawSegment( -- 320 - dora.Vec2(segment.startX, segment.startY), -- 321 - dora.Vec2(segment.stopX, segment.stopY), -- 322 - segment.radius, -- 323 - dora.Color(segment.color or 4294967295) -- 324 - ) -- 324 - break -- 326 - end -- 326 - end -- 326 - ____cond69 = ____cond69 or ____switch69 == "rect-shape" -- 326 - if ____cond69 then -- 326 - do -- 326 - local rect = child.props -- 329 - local centerX = rect.centerX or 0 -- 330 - local centerY = rect.centerY or 0 -- 331 - local hw = rect.width / 2 -- 332 - local hh = rect.height / 2 -- 333 - node:drawPolygon( -- 334 - { -- 335 - dora.Vec2(centerX - hw, centerY + hh), -- 336 - dora.Vec2(centerX + hw, centerY + hh), -- 337 - dora.Vec2(centerX + hw, centerY - hh), -- 338 - dora.Vec2(centerX - hw, centerY - hh) -- 339 - }, -- 339 - dora.Color(rect.fillColor or 4294967295), -- 341 - rect.borderWidth or 0, -- 342 - dora.Color(rect.borderColor or 4294967295) -- 343 - ) -- 343 - break -- 345 - end -- 345 - end -- 345 - ____cond69 = ____cond69 or ____switch69 == "polygon-shape" -- 345 - if ____cond69 then -- 345 - do -- 345 - local poly = child.props -- 348 - node:drawPolygon( -- 349 - poly.verts, -- 350 - dora.Color(poly.fillColor or 4294967295), -- 351 - poly.borderWidth or 0, -- 352 - dora.Color(poly.borderColor or 4294967295) -- 353 - ) -- 353 - break -- 355 - end -- 355 - end -- 355 - ____cond69 = ____cond69 or ____switch69 == "verts-shape" -- 355 - if ____cond69 then -- 355 - do -- 355 - local verts = child.props -- 358 - node:drawVertices(__TS__ArrayMap( -- 359 - verts.verts, -- 359 - function(____, ____bindingPattern0) -- 359 - local color -- 359 - local vert -- 359 - vert = ____bindingPattern0[1] -- 359 - color = ____bindingPattern0[2] -- 359 - return { -- 359 - vert, -- 359 - dora.Color(color) -- 359 - } -- 359 - end -- 359 - )) -- 359 - break -- 360 - end -- 360 - end -- 360 - until true -- 360 - end -- 360 - ::__continue67:: -- 360 - end -- 360 - return cnode -- 364 - end -- 299 -end -- 299 -local getGrid -- 368 -do -- 368 - local function handleGridAttribute(cnode, _enode, k, v) -- 370 - repeat -- 370 - local ____switch78 = k -- 370 - local ____cond78 = ____switch78 == "file" or ____switch78 == "gridX" or ____switch78 == "gridY" -- 370 + cnode.blendFunc = v -- 293 + return true -- 293 + end -- 293 + until true -- 293 + return false -- 295 + end -- 290 + getDrawNode = function(enode) -- 297 + local node = dora.DrawNode() -- 298 + local cnode = getNode(enode, node, handleDrawNodeAttribute) -- 299 + local ____enode_7 = enode -- 300 + local children = ____enode_7.children -- 300 + for i = 1, #children do -- 300 + do -- 300 + local child = children[i] -- 302 + if type(child) ~= "table" then -- 302 + goto __continue67 -- 304 + end -- 304 + repeat -- 304 + local ____switch69 = child.type -- 304 + local ____cond69 = ____switch69 == "dot-shape" -- 304 + if ____cond69 then -- 304 + do -- 304 + local dot = child.props -- 308 + node:drawDot( -- 309 + dora.Vec2(dot.x or 0, dot.y or 0), -- 310 + dot.radius, -- 311 + dora.Color(dot.color or 4294967295) -- 312 + ) -- 312 + break -- 314 + end -- 314 + end -- 314 + ____cond69 = ____cond69 or ____switch69 == "segment-shape" -- 314 + if ____cond69 then -- 314 + do -- 314 + local segment = child.props -- 317 + node:drawSegment( -- 318 + dora.Vec2(segment.startX, segment.startY), -- 319 + dora.Vec2(segment.stopX, segment.stopY), -- 320 + segment.radius, -- 321 + dora.Color(segment.color or 4294967295) -- 322 + ) -- 322 + break -- 324 + end -- 324 + end -- 324 + ____cond69 = ____cond69 or ____switch69 == "rect-shape" -- 324 + if ____cond69 then -- 324 + do -- 324 + local rect = child.props -- 327 + local centerX = rect.centerX or 0 -- 328 + local centerY = rect.centerY or 0 -- 329 + local hw = rect.width / 2 -- 330 + local hh = rect.height / 2 -- 331 + node:drawPolygon( -- 332 + { -- 333 + dora.Vec2(centerX - hw, centerY + hh), -- 334 + dora.Vec2(centerX + hw, centerY + hh), -- 335 + dora.Vec2(centerX + hw, centerY - hh), -- 336 + dora.Vec2(centerX - hw, centerY - hh) -- 337 + }, -- 337 + dora.Color(rect.fillColor or 4294967295), -- 339 + rect.borderWidth or 0, -- 340 + dora.Color(rect.borderColor or 4294967295) -- 341 + ) -- 341 + break -- 343 + end -- 343 + end -- 343 + ____cond69 = ____cond69 or ____switch69 == "polygon-shape" -- 343 + if ____cond69 then -- 343 + do -- 343 + local poly = child.props -- 346 + node:drawPolygon( -- 347 + poly.verts, -- 348 + dora.Color(poly.fillColor or 4294967295), -- 349 + poly.borderWidth or 0, -- 350 + dora.Color(poly.borderColor or 4294967295) -- 351 + ) -- 351 + break -- 353 + end -- 353 + end -- 353 + ____cond69 = ____cond69 or ____switch69 == "verts-shape" -- 353 + if ____cond69 then -- 353 + do -- 353 + local verts = child.props -- 356 + node:drawVertices(__TS__ArrayMap( -- 357 + verts.verts, -- 357 + function(____, ____bindingPattern0) -- 357 + local color -- 357 + local vert -- 357 + vert = ____bindingPattern0[1] -- 357 + color = ____bindingPattern0[2] -- 357 + return { -- 357 + vert, -- 357 + dora.Color(color) -- 357 + } -- 357 + end -- 357 + )) -- 357 + break -- 358 + end -- 358 + end -- 358 + until true -- 358 + end -- 358 + ::__continue67:: -- 358 + end -- 358 + return cnode -- 362 + end -- 297 +end -- 297 +local getGrid -- 366 +do -- 366 + local function handleGridAttribute(cnode, _enode, k, v) -- 368 + repeat -- 368 + local ____switch78 = k -- 368 + local ____cond78 = ____switch78 == "file" or ____switch78 == "gridX" or ____switch78 == "gridY" -- 368 + if ____cond78 then -- 368 + return true -- 370 + end -- 370 + ____cond78 = ____cond78 or ____switch78 == "textureRect" -- 370 if ____cond78 then -- 370 + cnode.textureRect = v -- 371 + return true -- 371 + end -- 371 + ____cond78 = ____cond78 or ____switch78 == "depthWrite" -- 371 + if ____cond78 then -- 371 + cnode.depthWrite = v -- 372 return true -- 372 end -- 372 - ____cond78 = ____cond78 or ____switch78 == "textureRect" -- 372 + ____cond78 = ____cond78 or ____switch78 == "blendFunc" -- 372 if ____cond78 then -- 372 - cnode.textureRect = v -- 373 + cnode.blendFunc = v -- 373 return true -- 373 end -- 373 - ____cond78 = ____cond78 or ____switch78 == "depthWrite" -- 373 + ____cond78 = ____cond78 or ____switch78 == "effect" -- 373 if ____cond78 then -- 373 - cnode.depthWrite = v -- 374 + cnode.effect = v -- 374 return true -- 374 end -- 374 - ____cond78 = ____cond78 or ____switch78 == "blendFunc" -- 374 - if ____cond78 then -- 374 - cnode.blendFunc = v -- 375 - return true -- 375 - end -- 375 - ____cond78 = ____cond78 or ____switch78 == "effect" -- 375 - if ____cond78 then -- 375 - cnode.effect = v -- 376 - return true -- 376 - end -- 376 - until true -- 376 - return false -- 378 - end -- 370 - getGrid = function(enode) -- 380 - local grid = enode.props -- 381 - local node = dora.Grid(grid.file, grid.gridX, grid.gridY) -- 382 - local cnode = getNode(enode, node, handleGridAttribute) -- 383 - return cnode -- 384 - end -- 380 -end -- 380 -local getSprite -- 388 -do -- 388 - local function handleSpriteAttribute(cnode, _enode, k, v) -- 390 - repeat -- 390 - local ____switch82 = k -- 390 - local ____cond82 = ____switch82 == "file" -- 390 + until true -- 374 + return false -- 376 + end -- 368 + getGrid = function(enode) -- 378 + local grid = enode.props -- 379 + local node = dora.Grid(grid.file, grid.gridX, grid.gridY) -- 380 + local cnode = getNode(enode, node, handleGridAttribute) -- 381 + return cnode -- 382 + end -- 378 +end -- 378 +local getSprite -- 386 +do -- 386 + local function handleSpriteAttribute(cnode, _enode, k, v) -- 388 + repeat -- 388 + local ____switch82 = k -- 388 + local ____cond82 = ____switch82 == "file" -- 388 + if ____cond82 then -- 388 + return true -- 390 + end -- 390 + ____cond82 = ____cond82 or ____switch82 == "textureRect" -- 390 if ____cond82 then -- 390 + cnode.textureRect = v -- 391 + return true -- 391 + end -- 391 + ____cond82 = ____cond82 or ____switch82 == "depthWrite" -- 391 + if ____cond82 then -- 391 + cnode.depthWrite = v -- 392 return true -- 392 end -- 392 - ____cond82 = ____cond82 or ____switch82 == "textureRect" -- 392 + ____cond82 = ____cond82 or ____switch82 == "blendFunc" -- 392 if ____cond82 then -- 392 - cnode.textureRect = v -- 393 + cnode.blendFunc = v -- 393 return true -- 393 end -- 393 - ____cond82 = ____cond82 or ____switch82 == "depthWrite" -- 393 + ____cond82 = ____cond82 or ____switch82 == "effect" -- 393 if ____cond82 then -- 393 - cnode.depthWrite = v -- 394 + cnode.effect = v -- 394 return true -- 394 end -- 394 - ____cond82 = ____cond82 or ____switch82 == "blendFunc" -- 394 + ____cond82 = ____cond82 or ____switch82 == "alphaRef" -- 394 if ____cond82 then -- 394 - cnode.blendFunc = v -- 395 + cnode.alphaRef = v -- 395 return true -- 395 end -- 395 - ____cond82 = ____cond82 or ____switch82 == "effect" -- 395 + ____cond82 = ____cond82 or ____switch82 == "uwrap" -- 395 if ____cond82 then -- 395 - cnode.effect = v -- 396 + cnode.uwrap = v -- 396 return true -- 396 end -- 396 - ____cond82 = ____cond82 or ____switch82 == "alphaRef" -- 396 + ____cond82 = ____cond82 or ____switch82 == "vwrap" -- 396 if ____cond82 then -- 396 - cnode.alphaRef = v -- 397 + cnode.vwrap = v -- 397 return true -- 397 end -- 397 - ____cond82 = ____cond82 or ____switch82 == "uwrap" -- 397 + ____cond82 = ____cond82 or ____switch82 == "filter" -- 397 if ____cond82 then -- 397 - cnode.uwrap = v -- 398 + cnode.filter = v -- 398 return true -- 398 end -- 398 - ____cond82 = ____cond82 or ____switch82 == "vwrap" -- 398 - if ____cond82 then -- 398 - cnode.vwrap = v -- 399 - return true -- 399 - end -- 399 - ____cond82 = ____cond82 or ____switch82 == "filter" -- 399 - if ____cond82 then -- 399 - cnode.filter = v -- 400 - return true -- 400 - end -- 400 - until true -- 400 - return false -- 402 - end -- 390 - getSprite = function(enode) -- 404 - local sp = enode.props -- 405 - local node = dora.Sprite(sp.file) -- 406 - if node ~= nil then -- 406 - local cnode = getNode(enode, node, handleSpriteAttribute) -- 408 - return cnode -- 409 - end -- 409 - return nil -- 411 - end -- 404 -end -- 404 -local getLabel -- 415 -do -- 415 - local function handleLabelAttribute(cnode, _enode, k, v) -- 417 - repeat -- 417 - local ____switch87 = k -- 417 - local ____cond87 = ____switch87 == "fontName" or ____switch87 == "fontSize" or ____switch87 == "text" -- 417 + until true -- 398 + return false -- 400 + end -- 388 + getSprite = function(enode) -- 402 + local sp = enode.props -- 403 + local node = dora.Sprite(sp.file) -- 404 + if node ~= nil then -- 404 + local cnode = getNode(enode, node, handleSpriteAttribute) -- 406 + return cnode -- 407 + end -- 407 + return nil -- 409 + end -- 402 +end -- 402 +local getLabel -- 413 +do -- 413 + local function handleLabelAttribute(cnode, _enode, k, v) -- 415 + repeat -- 415 + local ____switch87 = k -- 415 + local ____cond87 = ____switch87 == "fontName" or ____switch87 == "fontSize" or ____switch87 == "text" -- 415 + if ____cond87 then -- 415 + return true -- 417 + end -- 417 + ____cond87 = ____cond87 or ____switch87 == "alphaRef" -- 417 if ____cond87 then -- 417 + cnode.alphaRef = v -- 418 + return true -- 418 + end -- 418 + ____cond87 = ____cond87 or ____switch87 == "textWidth" -- 418 + if ____cond87 then -- 418 + cnode.textWidth = v -- 419 return true -- 419 end -- 419 - ____cond87 = ____cond87 or ____switch87 == "alphaRef" -- 419 + ____cond87 = ____cond87 or ____switch87 == "lineGap" -- 419 if ____cond87 then -- 419 - cnode.alphaRef = v -- 420 + cnode.lineGap = v -- 420 return true -- 420 end -- 420 - ____cond87 = ____cond87 or ____switch87 == "textWidth" -- 420 + ____cond87 = ____cond87 or ____switch87 == "spacing" -- 420 if ____cond87 then -- 420 - cnode.textWidth = v -- 421 + cnode.spacing = v -- 421 return true -- 421 end -- 421 - ____cond87 = ____cond87 or ____switch87 == "lineGap" -- 421 + ____cond87 = ____cond87 or ____switch87 == "blendFunc" -- 421 if ____cond87 then -- 421 - cnode.lineGap = v -- 422 + cnode.blendFunc = v -- 422 return true -- 422 end -- 422 - ____cond87 = ____cond87 or ____switch87 == "spacing" -- 422 + ____cond87 = ____cond87 or ____switch87 == "depthWrite" -- 422 if ____cond87 then -- 422 - cnode.spacing = v -- 423 + cnode.depthWrite = v -- 423 return true -- 423 end -- 423 - ____cond87 = ____cond87 or ____switch87 == "blendFunc" -- 423 + ____cond87 = ____cond87 or ____switch87 == "batched" -- 423 if ____cond87 then -- 423 - cnode.blendFunc = v -- 424 + cnode.batched = v -- 424 return true -- 424 end -- 424 - ____cond87 = ____cond87 or ____switch87 == "depthWrite" -- 424 + ____cond87 = ____cond87 or ____switch87 == "effect" -- 424 if ____cond87 then -- 424 - cnode.depthWrite = v -- 425 + cnode.effect = v -- 425 return true -- 425 end -- 425 - ____cond87 = ____cond87 or ____switch87 == "batched" -- 425 + ____cond87 = ____cond87 or ____switch87 == "alignment" -- 425 if ____cond87 then -- 425 - cnode.batched = v -- 426 + cnode.alignment = v -- 426 return true -- 426 end -- 426 - ____cond87 = ____cond87 or ____switch87 == "effect" -- 426 - if ____cond87 then -- 426 - cnode.effect = v -- 427 - return true -- 427 - end -- 427 - ____cond87 = ____cond87 or ____switch87 == "alignment" -- 427 - if ____cond87 then -- 427 - cnode.alignment = v -- 428 - return true -- 428 - end -- 428 - until true -- 428 - return false -- 430 - end -- 417 - getLabel = function(enode) -- 432 - local label = enode.props -- 433 - local node = dora.Label(label.fontName, label.fontSize) -- 434 - if node ~= nil then -- 434 - local cnode = getNode(enode, node, handleLabelAttribute) -- 436 - local ____enode_8 = enode -- 437 - local children = ____enode_8.children -- 437 - local text = label.text or "" -- 438 - for i = 1, #children do -- 438 - local child = children[i] -- 440 - if type(child) ~= "table" then -- 440 - text = text .. tostring(child) -- 442 - end -- 442 - end -- 442 - node.text = text -- 445 - return cnode -- 446 - end -- 446 - return nil -- 448 - end -- 432 -end -- 432 -local getLine -- 452 -do -- 452 - local function handleLineAttribute(cnode, enode, k, v) -- 454 - local line = enode.props -- 455 - repeat -- 455 - local ____switch94 = k -- 455 - local ____cond94 = ____switch94 == "verts" -- 455 + until true -- 426 + return false -- 428 + end -- 415 + getLabel = function(enode) -- 430 + local label = enode.props -- 431 + local node = dora.Label(label.fontName, label.fontSize) -- 432 + if node ~= nil then -- 432 + local cnode = getNode(enode, node, handleLabelAttribute) -- 434 + local ____enode_8 = enode -- 435 + local children = ____enode_8.children -- 435 + local text = label.text or "" -- 436 + for i = 1, #children do -- 436 + local child = children[i] -- 438 + if type(child) ~= "table" then -- 438 + text = text .. tostring(child) -- 440 + end -- 440 + end -- 440 + node.text = text -- 443 + return cnode -- 444 + end -- 444 + return nil -- 446 + end -- 430 +end -- 430 +local getLine -- 450 +do -- 450 + local function handleLineAttribute(cnode, enode, k, v) -- 452 + local line = enode.props -- 453 + repeat -- 453 + local ____switch94 = k -- 453 + local ____cond94 = ____switch94 == "verts" -- 453 + if ____cond94 then -- 453 + cnode:set( -- 455 + v, -- 455 + dora.Color(line.lineColor or 4294967295) -- 455 + ) -- 455 + return true -- 455 + end -- 455 + ____cond94 = ____cond94 or ____switch94 == "depthWrite" -- 455 if ____cond94 then -- 455 - cnode:set( -- 457 - v, -- 457 - dora.Color(line.lineColor or 4294967295) -- 457 - ) -- 457 + cnode.depthWrite = v -- 456 + return true -- 456 + end -- 456 + ____cond94 = ____cond94 or ____switch94 == "blendFunc" -- 456 + if ____cond94 then -- 456 + cnode.blendFunc = v -- 457 return true -- 457 end -- 457 - ____cond94 = ____cond94 or ____switch94 == "depthWrite" -- 457 - if ____cond94 then -- 457 - cnode.depthWrite = v -- 458 - return true -- 458 - end -- 458 - ____cond94 = ____cond94 or ____switch94 == "blendFunc" -- 458 - if ____cond94 then -- 458 - cnode.blendFunc = v -- 459 - return true -- 459 - end -- 459 - until true -- 459 - return false -- 461 - end -- 454 - getLine = function(enode) -- 463 - local node = dora.Line() -- 464 - local cnode = getNode(enode, node, handleLineAttribute) -- 465 - return cnode -- 466 - end -- 463 -end -- 463 -local getParticle -- 470 -do -- 470 - local function handleParticleAttribute(cnode, _enode, k, v) -- 472 - repeat -- 472 - local ____switch98 = k -- 472 - local ____cond98 = ____switch98 == "file" -- 472 + until true -- 457 + return false -- 459 + end -- 452 + getLine = function(enode) -- 461 + local node = dora.Line() -- 462 + local cnode = getNode(enode, node, handleLineAttribute) -- 463 + return cnode -- 464 + end -- 461 +end -- 461 +local getParticle -- 468 +do -- 468 + local function handleParticleAttribute(cnode, _enode, k, v) -- 470 + repeat -- 470 + local ____switch98 = k -- 470 + local ____cond98 = ____switch98 == "file" -- 470 + if ____cond98 then -- 470 + return true -- 472 + end -- 472 + ____cond98 = ____cond98 or ____switch98 == "emit" -- 472 if ____cond98 then -- 472 + if v then -- 472 + cnode:start() -- 473 + end -- 473 + return true -- 473 + end -- 473 + ____cond98 = ____cond98 or ____switch98 == "onFinished" -- 473 + if ____cond98 then -- 473 + cnode:slot("Finished", v) -- 474 return true -- 474 end -- 474 - ____cond98 = ____cond98 or ____switch98 == "emit" -- 474 - if ____cond98 then -- 474 - if v then -- 474 - cnode:start() -- 475 - end -- 475 - return true -- 475 - end -- 475 - ____cond98 = ____cond98 or ____switch98 == "onFinished" -- 475 - if ____cond98 then -- 475 - cnode:slot("Finished", v) -- 476 - return true -- 476 - end -- 476 - until true -- 476 - return false -- 478 - end -- 472 - getParticle = function(enode) -- 480 - local particle = enode.props -- 481 - local node = dora.Particle(particle.file) -- 482 - if node ~= nil then -- 482 - local cnode = getNode(enode, node, handleParticleAttribute) -- 484 - return cnode -- 485 - end -- 485 - return nil -- 487 - end -- 480 -end -- 480 -local getMenu -- 491 -do -- 491 - local function handleMenuAttribute(cnode, _enode, k, v) -- 493 - repeat -- 493 - local ____switch104 = k -- 493 - local ____cond104 = ____switch104 == "enabled" -- 493 - if ____cond104 then -- 493 - cnode.enabled = v -- 495 - return true -- 495 - end -- 495 - until true -- 495 - return false -- 497 - end -- 493 - getMenu = function(enode) -- 499 - local node = dora.Menu() -- 500 - local cnode = getNode(enode, node, handleMenuAttribute) -- 501 - return cnode -- 502 - end -- 499 -end -- 499 -local getPhysicsWorld -- 506 -do -- 506 - local function handlePhysicsWorldAttribute(cnode, _enode, k, v) -- 508 - repeat -- 508 - local ____switch108 = k -- 508 - local ____cond108 = ____switch108 == "showDebug" -- 508 - if ____cond108 then -- 508 - cnode.showDebug = v -- 510 - return true -- 510 - end -- 510 - until true -- 510 - return false -- 512 - end -- 508 - getPhysicsWorld = function(enode) -- 514 - local node = dora.PhysicsWorld() -- 515 - local cnode = getNode(enode, node, handlePhysicsWorldAttribute) -- 516 - return cnode -- 517 - end -- 514 -end -- 514 -local getBody -- 521 -do -- 521 - local function handleBodyAttribute(cnode, _enode, k, v) -- 523 - repeat -- 523 - local ____switch112 = k -- 523 - local ____cond112 = ____switch112 == "type" or ____switch112 == "linearAcceleration" or ____switch112 == "fixedRotation" or ____switch112 == "bullet" or ____switch112 == "world" -- 523 - if ____cond112 then -- 523 + until true -- 474 + return false -- 476 + end -- 470 + getParticle = function(enode) -- 478 + local particle = enode.props -- 479 + local node = dora.Particle(particle.file) -- 480 + if node ~= nil then -- 480 + local cnode = getNode(enode, node, handleParticleAttribute) -- 482 + return cnode -- 483 + end -- 483 + return nil -- 485 + end -- 478 +end -- 478 +local getMenu -- 489 +do -- 489 + local function handleMenuAttribute(cnode, _enode, k, v) -- 491 + repeat -- 491 + local ____switch104 = k -- 491 + local ____cond104 = ____switch104 == "enabled" -- 491 + if ____cond104 then -- 491 + cnode.enabled = v -- 493 + return true -- 493 + end -- 493 + until true -- 493 + return false -- 495 + end -- 491 + getMenu = function(enode) -- 497 + local node = dora.Menu() -- 498 + local cnode = getNode(enode, node, handleMenuAttribute) -- 499 + return cnode -- 500 + end -- 497 +end -- 497 +local function getPhysicsWorld(enode) -- 504 + local node = dora.PhysicsWorld() -- 505 + local cnode = getNode(enode, node) -- 506 + return cnode -- 507 +end -- 504 +local getBody -- 510 +do -- 510 + local function handleBodyAttribute(cnode, _enode, k, v) -- 512 + repeat -- 512 + local ____switch109 = k -- 512 + local ____cond109 = ____switch109 == "type" or ____switch109 == "linearAcceleration" or ____switch109 == "fixedRotation" or ____switch109 == "bullet" or ____switch109 == "world" -- 512 + if ____cond109 then -- 512 + return true -- 519 + end -- 519 + ____cond109 = ____cond109 or ____switch109 == "velocityX" -- 519 + if ____cond109 then -- 519 + cnode.velocityX = v -- 520 + return true -- 520 + end -- 520 + ____cond109 = ____cond109 or ____switch109 == "velocityY" -- 520 + if ____cond109 then -- 520 + cnode.velocityY = v -- 521 + return true -- 521 + end -- 521 + ____cond109 = ____cond109 or ____switch109 == "angularRate" -- 521 + if ____cond109 then -- 521 + cnode.angularRate = v -- 522 + return true -- 522 + end -- 522 + ____cond109 = ____cond109 or ____switch109 == "group" -- 522 + if ____cond109 then -- 522 + cnode.group = v -- 523 + return true -- 523 + end -- 523 + ____cond109 = ____cond109 or ____switch109 == "linearDamping" -- 523 + if ____cond109 then -- 523 + cnode.linearDamping = v -- 524 + return true -- 524 + end -- 524 + ____cond109 = ____cond109 or ____switch109 == "angularDamping" -- 524 + if ____cond109 then -- 524 + cnode.angularDamping = v -- 525 + return true -- 525 + end -- 525 + ____cond109 = ____cond109 or ____switch109 == "owner" -- 525 + if ____cond109 then -- 525 + cnode.owner = v -- 526 + return true -- 526 + end -- 526 + ____cond109 = ____cond109 or ____switch109 == "receivingContact" -- 526 + if ____cond109 then -- 526 + cnode.receivingContact = v -- 527 + return true -- 527 + end -- 527 + ____cond109 = ____cond109 or ____switch109 == "onBodyEnter" -- 527 + if ____cond109 then -- 527 + cnode:slot("BodyEnter", v) -- 528 + return true -- 528 + end -- 528 + ____cond109 = ____cond109 or ____switch109 == "onBodyLeave" -- 528 + if ____cond109 then -- 528 + cnode:slot("BodyLeave", v) -- 529 + return true -- 529 + end -- 529 + ____cond109 = ____cond109 or ____switch109 == "onContactStart" -- 529 + if ____cond109 then -- 529 + cnode:slot("ContactStart", v) -- 530 return true -- 530 end -- 530 - ____cond112 = ____cond112 or ____switch112 == "velocityX" -- 530 - if ____cond112 then -- 530 - cnode.velocityX = v -- 531 + ____cond109 = ____cond109 or ____switch109 == "onContactEnd" -- 530 + if ____cond109 then -- 530 + cnode:slot("ContactEnd", v) -- 531 return true -- 531 end -- 531 - ____cond112 = ____cond112 or ____switch112 == "velocityY" -- 531 - if ____cond112 then -- 531 - cnode.velocityY = v -- 532 + ____cond109 = ____cond109 or ____switch109 == "onContactFilter" -- 531 + if ____cond109 then -- 531 + cnode:onContactFilter(v) -- 532 return true -- 532 end -- 532 - ____cond112 = ____cond112 or ____switch112 == "angularRate" -- 532 - if ____cond112 then -- 532 - cnode.angularRate = v -- 533 - return true -- 533 - end -- 533 - ____cond112 = ____cond112 or ____switch112 == "group" -- 533 - if ____cond112 then -- 533 - cnode.group = v -- 534 - return true -- 534 - end -- 534 - ____cond112 = ____cond112 or ____switch112 == "linearDamping" -- 534 - if ____cond112 then -- 534 - cnode.linearDamping = v -- 535 - return true -- 535 - end -- 535 - ____cond112 = ____cond112 or ____switch112 == "angularDamping" -- 535 - if ____cond112 then -- 535 - cnode.angularDamping = v -- 536 - return true -- 536 - end -- 536 - ____cond112 = ____cond112 or ____switch112 == "owner" -- 536 - if ____cond112 then -- 536 - cnode.owner = v -- 537 - return true -- 537 - end -- 537 - ____cond112 = ____cond112 or ____switch112 == "receivingContact" -- 537 - if ____cond112 then -- 537 - cnode.receivingContact = v -- 538 - return true -- 538 - end -- 538 - ____cond112 = ____cond112 or ____switch112 == "onBodyEnter" -- 538 - if ____cond112 then -- 538 - cnode:slot("BodyEnter", v) -- 539 - return true -- 539 - end -- 539 - ____cond112 = ____cond112 or ____switch112 == "onBodyLeave" -- 539 - if ____cond112 then -- 539 - cnode:slot("BodyLeave", v) -- 540 - return true -- 540 - end -- 540 - ____cond112 = ____cond112 or ____switch112 == "onContactStart" -- 540 - if ____cond112 then -- 540 - cnode:slot("ContactStart", v) -- 541 - return true -- 541 - end -- 541 - ____cond112 = ____cond112 or ____switch112 == "onContactEnd" -- 541 - if ____cond112 then -- 541 - cnode:slot("ContactEnd", v) -- 542 - return true -- 542 - end -- 542 - ____cond112 = ____cond112 or ____switch112 == "onContactFilter" -- 542 - if ____cond112 then -- 542 - cnode:onContactFilter(v) -- 543 - return true -- 543 - end -- 543 - until true -- 543 - return false -- 545 - end -- 523 - getBody = function(enode, world) -- 547 - local def = enode.props -- 548 - local bodyDef = dora.BodyDef() -- 549 - bodyDef.type = def.type -- 550 - if def.angle ~= nil then -- 550 - bodyDef.angle = def.angle -- 551 - end -- 551 - if def.angularDamping ~= nil then -- 551 - bodyDef.angularDamping = def.angularDamping -- 552 - end -- 552 - if def.bullet ~= nil then -- 552 - bodyDef.bullet = def.bullet -- 553 - end -- 553 - if def.fixedRotation ~= nil then -- 553 - bodyDef.fixedRotation = def.fixedRotation -- 554 - end -- 554 - bodyDef.linearAcceleration = def.linearAcceleration or dora.Vec2(0, -9.8) -- 555 - if def.linearDamping ~= nil then -- 555 - bodyDef.linearDamping = def.linearDamping -- 556 - end -- 556 - bodyDef.position = dora.Vec2(def.x or 0, def.y or 0) -- 557 - local extraSensors = nil -- 558 - for i = 1, #enode.children do -- 558 - do -- 558 - local child = enode.children[i] -- 560 - if type(child) ~= "table" then -- 560 - goto __continue119 -- 562 - end -- 562 - repeat -- 562 - local ____switch121 = child.type -- 562 - local ____cond121 = ____switch121 == "rect-fixture" -- 562 - if ____cond121 then -- 562 - do -- 562 - local shape = child.props -- 566 - if shape.sensorTag ~= nil then -- 566 - bodyDef:attachPolygonSensor( -- 568 - shape.sensorTag, -- 569 - dora.Vec2(shape.centerX or 0, shape.centerY or 0), -- 570 - shape.width, -- 571 - shape.height, -- 571 - shape.angle or 0 -- 572 - ) -- 572 - else -- 572 - bodyDef:attachPolygon( -- 575 - dora.Vec2(shape.centerX or 0, shape.centerY or 0), -- 576 - shape.width, -- 577 - shape.height, -- 577 - shape.angle or 0, -- 578 - shape.density or 1, -- 579 - shape.friction or 0.4, -- 580 - shape.restitution or 0 -- 581 - ) -- 581 - end -- 581 - break -- 584 - end -- 584 - end -- 584 - ____cond121 = ____cond121 or ____switch121 == "polygon-fixture" -- 584 - if ____cond121 then -- 584 - do -- 584 - local shape = child.props -- 587 - if shape.sensorTag ~= nil then -- 587 - bodyDef:attachPolygonSensor(shape.sensorTag, shape.verts) -- 589 - else -- 589 - bodyDef:attachPolygon(shape.verts, shape.density or 1, shape.friction or 0.4, shape.restitution or 0) -- 594 - end -- 594 - break -- 601 - end -- 601 - end -- 601 - ____cond121 = ____cond121 or ____switch121 == "multi-fixture" -- 601 - if ____cond121 then -- 601 - do -- 601 - local shape = child.props -- 604 - if shape.sensorTag ~= nil then -- 604 - if extraSensors == nil then -- 604 - extraSensors = {} -- 606 - end -- 606 - extraSensors[#extraSensors + 1] = { -- 607 - shape.sensorTag, -- 607 - dora.BodyDef:multi(shape.verts) -- 607 - } -- 607 - else -- 607 - bodyDef:attachMulti(shape.verts, shape.density or 1, shape.friction or 0.4, shape.restitution or 0) -- 609 - end -- 609 - break -- 616 - end -- 616 - end -- 616 - ____cond121 = ____cond121 or ____switch121 == "disk-fixture" -- 616 - if ____cond121 then -- 616 - do -- 616 - local shape = child.props -- 619 - if shape.sensorTag ~= nil then -- 619 - bodyDef:attachDiskSensor( -- 621 - shape.sensorTag, -- 622 - dora.Vec2(shape.centerX or 0, shape.centerY or 0), -- 623 - shape.radius -- 624 - ) -- 624 - else -- 624 - bodyDef:attachDisk( -- 627 - dora.Vec2(shape.centerX or 0, shape.centerY or 0), -- 628 - shape.radius, -- 629 - shape.density or 1, -- 630 - shape.friction or 0.4, -- 631 - shape.restitution or 0 -- 632 - ) -- 632 + until true -- 532 + return false -- 534 + end -- 512 + getBody = function(enode, world) -- 536 + local def = enode.props -- 537 + local bodyDef = dora.BodyDef() -- 538 + bodyDef.type = def.type -- 539 + if def.angle ~= nil then -- 539 + bodyDef.angle = def.angle -- 540 + end -- 540 + if def.angularDamping ~= nil then -- 540 + bodyDef.angularDamping = def.angularDamping -- 541 + end -- 541 + if def.bullet ~= nil then -- 541 + bodyDef.bullet = def.bullet -- 542 + end -- 542 + if def.fixedRotation ~= nil then -- 542 + bodyDef.fixedRotation = def.fixedRotation -- 543 + end -- 543 + bodyDef.linearAcceleration = def.linearAcceleration or dora.Vec2(0, -9.8) -- 544 + if def.linearDamping ~= nil then -- 544 + bodyDef.linearDamping = def.linearDamping -- 545 + end -- 545 + bodyDef.position = dora.Vec2(def.x or 0, def.y or 0) -- 546 + local extraSensors = nil -- 547 + for i = 1, #enode.children do -- 547 + do -- 547 + local child = enode.children[i] -- 549 + if type(child) ~= "table" then -- 549 + goto __continue116 -- 551 + end -- 551 + repeat -- 551 + local ____switch118 = child.type -- 551 + local ____cond118 = ____switch118 == "rect-fixture" -- 551 + if ____cond118 then -- 551 + do -- 551 + local shape = child.props -- 555 + if shape.sensorTag ~= nil then -- 555 + bodyDef:attachPolygonSensor( -- 557 + shape.sensorTag, -- 558 + dora.Vec2(shape.centerX or 0, shape.centerY or 0), -- 559 + shape.width, -- 560 + shape.height, -- 560 + shape.angle or 0 -- 561 + ) -- 561 + else -- 561 + bodyDef:attachPolygon( -- 564 + dora.Vec2(shape.centerX or 0, shape.centerY or 0), -- 565 + shape.width, -- 566 + shape.height, -- 566 + shape.angle or 0, -- 567 + shape.density or 1, -- 568 + shape.friction or 0.4, -- 569 + shape.restitution or 0 -- 570 + ) -- 570 + end -- 570 + break -- 573 + end -- 573 + end -- 573 + ____cond118 = ____cond118 or ____switch118 == "polygon-fixture" -- 573 + if ____cond118 then -- 573 + do -- 573 + local shape = child.props -- 576 + if shape.sensorTag ~= nil then -- 576 + bodyDef:attachPolygonSensor(shape.sensorTag, shape.verts) -- 578 + else -- 578 + bodyDef:attachPolygon(shape.verts, shape.density or 1, shape.friction or 0.4, shape.restitution or 0) -- 583 + end -- 583 + break -- 590 + end -- 590 + end -- 590 + ____cond118 = ____cond118 or ____switch118 == "multi-fixture" -- 590 + if ____cond118 then -- 590 + do -- 590 + local shape = child.props -- 593 + if shape.sensorTag ~= nil then -- 593 + if extraSensors == nil then -- 593 + extraSensors = {} -- 595 + end -- 595 + extraSensors[#extraSensors + 1] = { -- 596 + shape.sensorTag, -- 596 + dora.BodyDef:multi(shape.verts) -- 596 + } -- 596 + else -- 596 + bodyDef:attachMulti(shape.verts, shape.density or 1, shape.friction or 0.4, shape.restitution or 0) -- 598 + end -- 598 + break -- 605 + end -- 605 + end -- 605 + ____cond118 = ____cond118 or ____switch118 == "disk-fixture" -- 605 + if ____cond118 then -- 605 + do -- 605 + local shape = child.props -- 608 + if shape.sensorTag ~= nil then -- 608 + bodyDef:attachDiskSensor( -- 610 + shape.sensorTag, -- 611 + dora.Vec2(shape.centerX or 0, shape.centerY or 0), -- 612 + shape.radius -- 613 + ) -- 613 + else -- 613 + bodyDef:attachDisk( -- 616 + dora.Vec2(shape.centerX or 0, shape.centerY or 0), -- 617 + shape.radius, -- 618 + shape.density or 1, -- 619 + shape.friction or 0.4, -- 620 + shape.restitution or 0 -- 621 + ) -- 621 + end -- 621 + break -- 624 + end -- 624 + end -- 624 + ____cond118 = ____cond118 or ____switch118 == "chain-fixture" -- 624 + if ____cond118 then -- 624 + do -- 624 + local shape = child.props -- 627 + if shape.sensorTag ~= nil then -- 627 + if extraSensors == nil then -- 627 + extraSensors = {} -- 629 + end -- 629 + extraSensors[#extraSensors + 1] = { -- 630 + shape.sensorTag, -- 630 + dora.BodyDef:chain(shape.verts) -- 630 + } -- 630 + else -- 630 + bodyDef:attachChain(shape.verts, shape.friction or 0.4, shape.restitution or 0) -- 632 end -- 632 - break -- 635 - end -- 635 - end -- 635 - ____cond121 = ____cond121 or ____switch121 == "chain-fixture" -- 635 - if ____cond121 then -- 635 - do -- 635 - local shape = child.props -- 638 - if shape.sensorTag ~= nil then -- 638 - if extraSensors == nil then -- 638 - extraSensors = {} -- 640 - end -- 640 - extraSensors[#extraSensors + 1] = { -- 641 - shape.sensorTag, -- 641 - dora.BodyDef:chain(shape.verts) -- 641 - } -- 641 - else -- 641 - bodyDef:attachChain(shape.verts, shape.friction or 0.4, shape.restitution or 0) -- 643 - end -- 643 - break -- 649 - end -- 649 - end -- 649 - until true -- 649 - end -- 649 - ::__continue119:: -- 649 - end -- 649 - local body = dora.Body(bodyDef, world) -- 653 - if extraSensors ~= nil then -- 653 - for i = 1, #extraSensors do -- 653 - local tag, def = table.unpack(extraSensors[i]) -- 656 - body:attachSensor(tag, def) -- 657 - end -- 657 - end -- 657 - local cnode = getNode(enode, body, handleBodyAttribute) -- 660 - if def.receivingContact ~= false and (def.onBodyEnter or def.onBodyLeave or def.onContactStart or def.onContactEnd) then -- 660 - body.receivingContact = true -- 667 - end -- 667 - return cnode -- 669 - end -- 547 -end -- 547 -local getCustomNode -- 673 -do -- 673 - local function handleCustomNode(_cnode, _enode, k, _v) -- 675 - repeat -- 675 - local ____switch142 = k -- 675 - local ____cond142 = ____switch142 == "onCreate" -- 675 - if ____cond142 then -- 675 - return true -- 677 - end -- 677 - until true -- 677 - return false -- 679 - end -- 675 - getCustomNode = function(enode) -- 681 - local custom = enode.props -- 682 - local node = custom.onCreate() -- 683 - if node then -- 683 - local cnode = getNode(enode, node, handleCustomNode) -- 685 - return cnode -- 686 - end -- 686 - return nil -- 688 - end -- 681 -end -- 681 -local function addChild(nodeStack, cnode, enode) -- 692 - if #nodeStack > 0 then -- 692 - local last = nodeStack[#nodeStack] -- 694 - last:addChild(cnode) -- 695 - end -- 695 - nodeStack[#nodeStack + 1] = cnode -- 697 - local ____enode_9 = enode -- 698 - local children = ____enode_9.children -- 698 - for i = 1, #children do -- 698 - visitNode(nodeStack, children[i], enode) -- 700 - end -- 700 - if #nodeStack > 1 then -- 700 - table.remove(nodeStack) -- 703 - end -- 703 -end -- 692 -local function drawNodeCheck(_nodeStack, enode, parent) -- 711 - if parent == nil or parent.type ~= "draw-node" then -- 711 - Warn(("label <" .. enode.type) .. "> must be placed under a to take effect") -- 713 - end -- 713 -end -- 711 -local function visitAction(actionStack, enode) -- 717 - local createAction = actionMap[enode.type] -- 718 - if createAction ~= nil then -- 718 - actionStack[#actionStack + 1] = createAction(enode.props.time, enode.props.start, enode.props.stop, enode.props.easing) -- 720 - return -- 721 - end -- 721 - repeat -- 721 - local ____switch153 = enode.type -- 721 - local ____cond153 = ____switch153 == "delay" -- 721 - if ____cond153 then -- 721 - do -- 721 - local item = enode.props -- 725 - actionStack[#actionStack + 1] = dora.Delay(item.time) -- 726 - break -- 727 - end -- 727 - end -- 727 - ____cond153 = ____cond153 or ____switch153 == "event" -- 727 - if ____cond153 then -- 727 - do -- 727 - local item = enode.props -- 730 - actionStack[#actionStack + 1] = dora.Event(item.name, item.param) -- 731 - break -- 732 - end -- 732 - end -- 732 - ____cond153 = ____cond153 or ____switch153 == "hide" -- 732 - if ____cond153 then -- 732 - do -- 732 - actionStack[#actionStack + 1] = dora.Hide() -- 735 - break -- 736 - end -- 736 - end -- 736 - ____cond153 = ____cond153 or ____switch153 == "show" -- 736 - if ____cond153 then -- 736 - do -- 736 - actionStack[#actionStack + 1] = dora.Show() -- 739 - break -- 740 - end -- 740 - end -- 740 - ____cond153 = ____cond153 or ____switch153 == "move" -- 740 - if ____cond153 then -- 740 - do -- 740 - local item = enode.props -- 743 - actionStack[#actionStack + 1] = dora.Move( -- 744 - item.time, -- 744 - dora.Vec2(item.startX, item.startY), -- 744 - dora.Vec2(item.stopX, item.stopY), -- 744 - item.easing -- 744 - ) -- 744 - break -- 745 - end -- 745 - end -- 745 - ____cond153 = ____cond153 or ____switch153 == "spawn" -- 745 - if ____cond153 then -- 745 - do -- 745 - local spawnStack = {} -- 748 - for i = 1, #enode.children do -- 748 - visitAction(spawnStack, enode.children[i]) -- 750 - end -- 750 - actionStack[#actionStack + 1] = dora.Spawn(table.unpack(spawnStack)) -- 752 - break -- 753 - end -- 753 - end -- 753 - ____cond153 = ____cond153 or ____switch153 == "sequence" -- 753 - if ____cond153 then -- 753 - do -- 753 - local sequenceStack = {} -- 756 - for i = 1, #enode.children do -- 756 - visitAction(sequenceStack, enode.children[i]) -- 758 - end -- 758 - actionStack[#actionStack + 1] = dora.Sequence(table.unpack(sequenceStack)) -- 760 + break -- 638 + end -- 638 + end -- 638 + until true -- 638 + end -- 638 + ::__continue116:: -- 638 + end -- 638 + local body = dora.Body(bodyDef, world) -- 642 + if extraSensors ~= nil then -- 642 + for i = 1, #extraSensors do -- 642 + local tag, def = table.unpack(extraSensors[i]) -- 645 + body:attachSensor(tag, def) -- 646 + end -- 646 + end -- 646 + local cnode = getNode(enode, body, handleBodyAttribute) -- 649 + if def.receivingContact ~= false and (def.onBodyEnter or def.onBodyLeave or def.onContactStart or def.onContactEnd) then -- 649 + body.receivingContact = true -- 656 + end -- 656 + return cnode -- 658 + end -- 536 +end -- 536 +local getCustomNode -- 662 +do -- 662 + local function handleCustomNode(_cnode, _enode, k, _v) -- 664 + repeat -- 664 + local ____switch139 = k -- 664 + local ____cond139 = ____switch139 == "onCreate" -- 664 + if ____cond139 then -- 664 + return true -- 666 + end -- 666 + until true -- 666 + return false -- 668 + end -- 664 + getCustomNode = function(enode) -- 670 + local custom = enode.props -- 671 + local node = custom.onCreate() -- 672 + if node then -- 672 + local cnode = getNode(enode, node, handleCustomNode) -- 674 + return cnode -- 675 + end -- 675 + return nil -- 677 + end -- 670 +end -- 670 +local getAlignNode -- 681 +do -- 681 + local function handleAlignNode(_cnode, _enode, k, _v) -- 683 + repeat -- 683 + local ____switch144 = k -- 683 + local ____cond144 = ____switch144 == "windowRoot" -- 683 + if ____cond144 then -- 683 + return true -- 685 + end -- 685 + ____cond144 = ____cond144 or ____switch144 == "style" -- 685 + if ____cond144 then -- 685 + return true -- 686 + end -- 686 + ____cond144 = ____cond144 or ____switch144 == "onLayout" -- 686 + if ____cond144 then -- 686 + return true -- 687 + end -- 687 + until true -- 687 + return false -- 689 + end -- 683 + getAlignNode = function(enode) -- 691 + local alignNode = enode.props -- 692 + local node = dora.AlignNode(alignNode.windowRoot) -- 693 + if alignNode.style then -- 693 + local items = {} -- 695 + for k, v in pairs(alignNode.style) do -- 696 + local name = string.gsub(k, "%u", "-%1") -- 697 + name = string.lower(name) -- 698 + repeat -- 698 + local ____switch148 = k -- 698 + local ____cond148 = ____switch148 == "margin" or ____switch148 == "padding" or ____switch148 == "border" or ____switch148 == "gap" -- 698 + if ____cond148 then -- 698 + do -- 698 + if type(v) == "table" then -- 698 + local valueStr = table.concat( -- 703 + __TS__ArrayMap( -- 703 + v, -- 703 + function(____, item) return tostring(item) end -- 703 + ), -- 703 + "," -- 703 + ) -- 703 + items[#items + 1] = (name .. ":") .. valueStr -- 704 + else -- 704 + items[#items + 1] = (name .. ":") .. tostring(v) -- 706 + end -- 706 + break -- 708 + end -- 708 + end -- 708 + do -- 708 + items[#items + 1] = (name .. ":") .. tostring(v) -- 711 + break -- 712 + end -- 712 + until true -- 712 + end -- 712 + if alignNode.onLayout then -- 712 + node:slot("AlignLayout", alignNode.onLayout) -- 716 + end -- 716 + local styleStr = table.concat(items, ";") -- 718 + node:css(styleStr) -- 719 + end -- 719 + local cnode = getNode(enode, node, handleAlignNode) -- 721 + return cnode -- 722 + end -- 691 +end -- 691 +local function addChild(nodeStack, cnode, enode) -- 726 + if #nodeStack > 0 then -- 726 + local last = nodeStack[#nodeStack] -- 728 + last:addChild(cnode) -- 729 + end -- 729 + nodeStack[#nodeStack + 1] = cnode -- 731 + local ____enode_9 = enode -- 732 + local children = ____enode_9.children -- 732 + for i = 1, #children do -- 732 + visitNode(nodeStack, children[i], enode) -- 734 + end -- 734 + if #nodeStack > 1 then -- 734 + table.remove(nodeStack) -- 737 + end -- 737 +end -- 726 +local function drawNodeCheck(_nodeStack, enode, parent) -- 745 + if parent == nil or parent.type ~= "draw-node" then -- 745 + Warn(("label <" .. enode.type) .. "> must be placed under a to take effect") -- 747 + end -- 747 +end -- 745 +local function visitAction(actionStack, enode) -- 751 + local createAction = actionMap[enode.type] -- 752 + if createAction ~= nil then -- 752 + actionStack[#actionStack + 1] = createAction(enode.props.time, enode.props.start, enode.props.stop, enode.props.easing) -- 754 + return -- 755 + end -- 755 + repeat -- 755 + local ____switch162 = enode.type -- 755 + local ____cond162 = ____switch162 == "delay" -- 755 + if ____cond162 then -- 755 + do -- 755 + local item = enode.props -- 759 + actionStack[#actionStack + 1] = dora.Delay(item.time) -- 760 break -- 761 end -- 761 end -- 761 - do -- 761 - Warn(("unsupported tag <" .. enode.type) .. "> under action definition") -- 764 - break -- 765 - end -- 765 - until true -- 765 -end -- 717 -local function actionCheck(nodeStack, enode, parent) -- 769 - local unsupported = false -- 770 - if parent == nil then -- 770 - unsupported = true -- 772 - else -- 772 - repeat -- 772 - local ____switch166 = parent.type -- 772 - local ____cond166 = ____switch166 == "action" or ____switch166 == "spawn" or ____switch166 == "sequence" -- 772 - if ____cond166 then -- 772 - break -- 775 - end -- 775 - do -- 775 - unsupported = true -- 776 - break -- 776 - end -- 776 - until true -- 776 - end -- 776 - if unsupported then -- 776 - if #nodeStack > 0 then -- 776 - local node = nodeStack[#nodeStack] -- 781 - local actionStack = {} -- 782 - visitAction(actionStack, enode) -- 783 - if #actionStack == 1 then -- 783 - node:runAction(actionStack[1]) -- 785 - end -- 785 - else -- 785 - Warn(("tag <" .. enode.type) .. "> must be placed under , , or other scene node to take effect") -- 788 - end -- 788 - end -- 788 -end -- 769 -local function bodyCheck(_nodeStack, enode, parent) -- 793 - if parent == nil or parent.type ~= "body" then -- 793 - Warn(("label <" .. enode.type) .. "> must be placed under a to take effect") -- 795 - end -- 795 -end -- 793 -actionMap = { -- 799 - ["anchor-x"] = dora.AnchorX, -- 802 - ["anchor-y"] = dora.AnchorY, -- 803 - angle = dora.Angle, -- 804 - ["angle-x"] = dora.AngleX, -- 805 - ["angle-y"] = dora.AngleY, -- 806 - width = dora.Width, -- 807 - height = dora.Height, -- 808 - opacity = dora.Opacity, -- 809 - roll = dora.Roll, -- 810 - scale = dora.Scale, -- 811 - ["scale-x"] = dora.ScaleX, -- 812 - ["scale-y"] = dora.ScaleY, -- 813 - ["skew-x"] = dora.SkewX, -- 814 - ["skew-y"] = dora.SkewY, -- 815 - ["move-x"] = dora.X, -- 816 - ["move-y"] = dora.Y, -- 817 - ["move-z"] = dora.Z -- 818 -} -- 818 -elementMap = { -- 821 - node = function(nodeStack, enode, parent) -- 822 - addChild( -- 823 - nodeStack, -- 823 - getNode(enode), -- 823 - enode -- 823 - ) -- 823 - end, -- 822 - ["clip-node"] = function(nodeStack, enode, parent) -- 825 - addChild( -- 826 - nodeStack, -- 826 - getClipNode(enode), -- 826 - enode -- 826 - ) -- 826 - end, -- 825 - playable = function(nodeStack, enode, parent) -- 828 - local cnode = getPlayable(enode) -- 829 - if cnode ~= nil then -- 829 - addChild(nodeStack, cnode, enode) -- 831 - end -- 831 - end, -- 828 - ["dragon-bone"] = function(nodeStack, enode, parent) -- 834 - local cnode = getDragonBone(enode) -- 835 - if cnode ~= nil then -- 835 - addChild(nodeStack, cnode, enode) -- 837 - end -- 837 - end, -- 834 - spine = function(nodeStack, enode, parent) -- 840 - local cnode = getSpine(enode) -- 841 - if cnode ~= nil then -- 841 - addChild(nodeStack, cnode, enode) -- 843 - end -- 843 - end, -- 840 - model = function(nodeStack, enode, parent) -- 846 - local cnode = getModel(enode) -- 847 - if cnode ~= nil then -- 847 - addChild(nodeStack, cnode, enode) -- 849 - end -- 849 - end, -- 846 - ["draw-node"] = function(nodeStack, enode, parent) -- 852 - addChild( -- 853 - nodeStack, -- 853 - getDrawNode(enode), -- 853 - enode -- 853 - ) -- 853 - end, -- 852 - ["dot-shape"] = drawNodeCheck, -- 855 - ["segment-shape"] = drawNodeCheck, -- 856 - ["rect-shape"] = drawNodeCheck, -- 857 - ["polygon-shape"] = drawNodeCheck, -- 858 - ["verts-shape"] = drawNodeCheck, -- 859 - grid = function(nodeStack, enode, parent) -- 860 - addChild( -- 861 - nodeStack, -- 861 - getGrid(enode), -- 861 - enode -- 861 - ) -- 861 - end, -- 860 - sprite = function(nodeStack, enode, parent) -- 863 - local cnode = getSprite(enode) -- 864 - if cnode ~= nil then -- 864 - addChild(nodeStack, cnode, enode) -- 866 - end -- 866 - end, -- 863 - label = function(nodeStack, enode, parent) -- 869 - local cnode = getLabel(enode) -- 870 - if cnode ~= nil then -- 870 - addChild(nodeStack, cnode, enode) -- 872 - end -- 872 - end, -- 869 - line = function(nodeStack, enode, parent) -- 875 - addChild( -- 876 - nodeStack, -- 876 - getLine(enode), -- 876 - enode -- 876 - ) -- 876 - end, -- 875 - particle = function(nodeStack, enode, parent) -- 878 - local cnode = getParticle(enode) -- 879 - if cnode ~= nil then -- 879 - addChild(nodeStack, cnode, enode) -- 881 - end -- 881 - end, -- 878 - menu = function(nodeStack, enode, parent) -- 884 - addChild( -- 885 - nodeStack, -- 885 - getMenu(enode), -- 885 - enode -- 885 - ) -- 885 - end, -- 884 - action = function(_nodeStack, enode, parent) -- 887 - if #enode.children == 0 then -- 887 - Warn(" tag has no children") -- 889 - return -- 890 - end -- 890 - local action = enode.props -- 892 - if action.ref == nil then -- 892 - Warn(" tag has no ref") -- 894 - return -- 895 - end -- 895 - local actionStack = {} -- 897 - for i = 1, #enode.children do -- 897 - visitAction(actionStack, enode.children[i]) -- 899 - end -- 899 - if #actionStack == 1 then -- 899 - action.ref.current = actionStack[1] -- 902 - elseif #actionStack > 1 then -- 902 - action.ref.current = dora.Sequence(table.unpack(actionStack)) -- 904 - end -- 904 - end, -- 887 - ["anchor-x"] = actionCheck, -- 907 - ["anchor-y"] = actionCheck, -- 908 - angle = actionCheck, -- 909 - ["angle-x"] = actionCheck, -- 910 - ["angle-y"] = actionCheck, -- 911 - delay = actionCheck, -- 912 - event = actionCheck, -- 913 - width = actionCheck, -- 914 - height = actionCheck, -- 915 - hide = actionCheck, -- 916 - show = actionCheck, -- 917 - move = actionCheck, -- 918 - opacity = actionCheck, -- 919 - roll = actionCheck, -- 920 - scale = actionCheck, -- 921 - ["scale-x"] = actionCheck, -- 922 - ["scale-y"] = actionCheck, -- 923 - ["skew-x"] = actionCheck, -- 924 - ["skew-y"] = actionCheck, -- 925 - ["move-x"] = actionCheck, -- 926 - ["move-y"] = actionCheck, -- 927 - ["move-z"] = actionCheck, -- 928 - spawn = actionCheck, -- 929 - sequence = actionCheck, -- 930 - loop = function(nodeStack, enode, _parent) -- 931 - if #nodeStack > 0 then -- 931 - local node = nodeStack[#nodeStack] -- 933 - local actionStack = {} -- 934 - for i = 1, #enode.children do -- 934 - visitAction(actionStack, enode.children[i]) -- 936 - end -- 936 - if #actionStack == 1 then -- 936 - node:runAction(actionStack[1], true) -- 939 - else -- 939 - local loop = enode.props -- 941 - if loop.spawn then -- 941 - node:runAction( -- 943 - dora.Spawn(table.unpack(actionStack)), -- 943 - true -- 943 - ) -- 943 - else -- 943 - node:runAction( -- 945 - dora.Sequence(table.unpack(actionStack)), -- 945 - true -- 945 - ) -- 945 - end -- 945 - end -- 945 - else -- 945 - Warn("tag must be placed under a scene node to take effect") -- 949 - end -- 949 - end, -- 931 - ["physics-world"] = function(nodeStack, enode, _parent) -- 952 - addChild( -- 953 - nodeStack, -- 953 - getPhysicsWorld(enode), -- 953 - enode -- 953 - ) -- 953 - end, -- 952 - contact = function(nodeStack, enode, _parent) -- 955 - local world = dora.tolua.cast(nodeStack[#nodeStack], "PhysicsWorld") -- 956 - if world ~= nil then -- 956 - local contact = enode.props -- 958 - world:setShouldContact(contact.groupA, contact.groupB, contact.enabled) -- 959 - else -- 959 - Warn(("tag <" .. enode.type) .. "> must be placed under or its derivatives to take effect") -- 961 - end -- 961 - end, -- 955 - body = function(nodeStack, enode, _parent) -- 964 - local def = enode.props -- 965 - if def.world then -- 965 - addChild( -- 967 - nodeStack, -- 967 - getBody(enode, def.world), -- 967 - enode -- 967 - ) -- 967 - return -- 968 - end -- 968 - local world = dora.tolua.cast(nodeStack[#nodeStack], "PhysicsWorld") -- 970 - if world ~= nil then -- 970 - addChild( -- 972 - nodeStack, -- 972 - getBody(enode, world), -- 972 - enode -- 972 - ) -- 972 - else -- 972 - Warn(("tag <" .. enode.type) .. "> must be placed under or its derivatives to take effect") -- 974 - end -- 974 - end, -- 964 - ["rect-fixture"] = bodyCheck, -- 977 - ["polygon-fixture"] = bodyCheck, -- 978 - ["multi-fixture"] = bodyCheck, -- 979 - ["disk-fixture"] = bodyCheck, -- 980 - ["chain-fixture"] = bodyCheck, -- 981 - ["distance-joint"] = function(_nodeStack, enode, _parent) -- 982 - local joint = enode.props -- 983 - if joint.ref == nil then -- 983 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 985 - return -- 986 - end -- 986 - if joint.bodyA.current == nil then -- 986 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 989 - return -- 990 - end -- 990 - if joint.bodyB.current == nil then -- 990 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 993 - return -- 994 - end -- 994 - local ____joint_ref_13 = joint.ref -- 996 - local ____self_11 = dora.Joint -- 996 - local ____self_11_distance_12 = ____self_11.distance -- 996 - local ____joint_canCollide_10 = joint.canCollide -- 997 - if ____joint_canCollide_10 == nil then -- 997 - ____joint_canCollide_10 = false -- 997 - end -- 997 - ____joint_ref_13.current = ____self_11_distance_12( -- 996 - ____self_11, -- 996 - ____joint_canCollide_10, -- 997 - joint.bodyA.current, -- 998 - joint.bodyB.current, -- 999 - joint.anchorA or dora.Vec2.zero, -- 1000 - joint.anchorB or dora.Vec2.zero, -- 1001 - joint.frequency or 0, -- 1002 - joint.damping or 0 -- 1003 - ) -- 1003 - end, -- 982 - ["friction-joint"] = function(_nodeStack, enode, _parent) -- 1005 - local joint = enode.props -- 1006 - if joint.ref == nil then -- 1006 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1008 - return -- 1009 - end -- 1009 - if joint.bodyA.current == nil then -- 1009 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1012 - return -- 1013 - end -- 1013 - if joint.bodyB.current == nil then -- 1013 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1016 - return -- 1017 - end -- 1017 - local ____joint_ref_17 = joint.ref -- 1019 - local ____self_15 = dora.Joint -- 1019 - local ____self_15_friction_16 = ____self_15.friction -- 1019 - local ____joint_canCollide_14 = joint.canCollide -- 1020 - if ____joint_canCollide_14 == nil then -- 1020 - ____joint_canCollide_14 = false -- 1020 + ____cond162 = ____cond162 or ____switch162 == "event" -- 761 + if ____cond162 then -- 761 + do -- 761 + local item = enode.props -- 764 + actionStack[#actionStack + 1] = dora.Event(item.name, item.param) -- 765 + break -- 766 + end -- 766 + end -- 766 + ____cond162 = ____cond162 or ____switch162 == "hide" -- 766 + if ____cond162 then -- 766 + do -- 766 + actionStack[#actionStack + 1] = dora.Hide() -- 769 + break -- 770 + end -- 770 + end -- 770 + ____cond162 = ____cond162 or ____switch162 == "show" -- 770 + if ____cond162 then -- 770 + do -- 770 + actionStack[#actionStack + 1] = dora.Show() -- 773 + break -- 774 + end -- 774 + end -- 774 + ____cond162 = ____cond162 or ____switch162 == "move" -- 774 + if ____cond162 then -- 774 + do -- 774 + local item = enode.props -- 777 + actionStack[#actionStack + 1] = dora.Move( -- 778 + item.time, -- 778 + dora.Vec2(item.startX, item.startY), -- 778 + dora.Vec2(item.stopX, item.stopY), -- 778 + item.easing -- 778 + ) -- 778 + break -- 779 + end -- 779 + end -- 779 + ____cond162 = ____cond162 or ____switch162 == "spawn" -- 779 + if ____cond162 then -- 779 + do -- 779 + local spawnStack = {} -- 782 + for i = 1, #enode.children do -- 782 + visitAction(spawnStack, enode.children[i]) -- 784 + end -- 784 + actionStack[#actionStack + 1] = dora.Spawn(table.unpack(spawnStack)) -- 786 + break -- 787 + end -- 787 + end -- 787 + ____cond162 = ____cond162 or ____switch162 == "sequence" -- 787 + if ____cond162 then -- 787 + do -- 787 + local sequenceStack = {} -- 790 + for i = 1, #enode.children do -- 790 + visitAction(sequenceStack, enode.children[i]) -- 792 + end -- 792 + actionStack[#actionStack + 1] = dora.Sequence(table.unpack(sequenceStack)) -- 794 + break -- 795 + end -- 795 + end -- 795 + do -- 795 + Warn(("unsupported tag <" .. enode.type) .. "> under action definition") -- 798 + break -- 799 + end -- 799 + until true -- 799 +end -- 751 +local function actionCheck(nodeStack, enode, parent) -- 803 + local unsupported = false -- 804 + if parent == nil then -- 804 + unsupported = true -- 806 + else -- 806 + repeat -- 806 + local ____switch175 = parent.type -- 806 + local ____cond175 = ____switch175 == "action" or ____switch175 == "spawn" or ____switch175 == "sequence" -- 806 + if ____cond175 then -- 806 + break -- 809 + end -- 809 + do -- 809 + unsupported = true -- 810 + break -- 810 + end -- 810 + until true -- 810 + end -- 810 + if unsupported then -- 810 + if #nodeStack > 0 then -- 810 + local node = nodeStack[#nodeStack] -- 815 + local actionStack = {} -- 816 + visitAction(actionStack, enode) -- 817 + if #actionStack == 1 then -- 817 + node:runAction(actionStack[1]) -- 819 + end -- 819 + else -- 819 + Warn(("tag <" .. enode.type) .. "> must be placed under , , or other scene node to take effect") -- 822 + end -- 822 + end -- 822 +end -- 803 +local function bodyCheck(_nodeStack, enode, parent) -- 827 + if parent == nil or parent.type ~= "body" then -- 827 + Warn(("label <" .. enode.type) .. "> must be placed under a to take effect") -- 829 + end -- 829 +end -- 827 +actionMap = { -- 833 + ["anchor-x"] = dora.AnchorX, -- 836 + ["anchor-y"] = dora.AnchorY, -- 837 + angle = dora.Angle, -- 838 + ["angle-x"] = dora.AngleX, -- 839 + ["angle-y"] = dora.AngleY, -- 840 + width = dora.Width, -- 841 + height = dora.Height, -- 842 + opacity = dora.Opacity, -- 843 + roll = dora.Roll, -- 844 + scale = dora.Scale, -- 845 + ["scale-x"] = dora.ScaleX, -- 846 + ["scale-y"] = dora.ScaleY, -- 847 + ["skew-x"] = dora.SkewX, -- 848 + ["skew-y"] = dora.SkewY, -- 849 + ["move-x"] = dora.X, -- 850 + ["move-y"] = dora.Y, -- 851 + ["move-z"] = dora.Z -- 852 +} -- 852 +elementMap = { -- 855 + node = function(nodeStack, enode, parent) -- 856 + addChild( -- 857 + nodeStack, -- 857 + getNode(enode), -- 857 + enode -- 857 + ) -- 857 + end, -- 856 + ["clip-node"] = function(nodeStack, enode, parent) -- 859 + addChild( -- 860 + nodeStack, -- 860 + getClipNode(enode), -- 860 + enode -- 860 + ) -- 860 + end, -- 859 + playable = function(nodeStack, enode, parent) -- 862 + local cnode = getPlayable(enode) -- 863 + if cnode ~= nil then -- 863 + addChild(nodeStack, cnode, enode) -- 865 + end -- 865 + end, -- 862 + ["dragon-bone"] = function(nodeStack, enode, parent) -- 868 + local cnode = getDragonBone(enode) -- 869 + if cnode ~= nil then -- 869 + addChild(nodeStack, cnode, enode) -- 871 + end -- 871 + end, -- 868 + spine = function(nodeStack, enode, parent) -- 874 + local cnode = getSpine(enode) -- 875 + if cnode ~= nil then -- 875 + addChild(nodeStack, cnode, enode) -- 877 + end -- 877 + end, -- 874 + model = function(nodeStack, enode, parent) -- 880 + local cnode = getModel(enode) -- 881 + if cnode ~= nil then -- 881 + addChild(nodeStack, cnode, enode) -- 883 + end -- 883 + end, -- 880 + ["draw-node"] = function(nodeStack, enode, parent) -- 886 + addChild( -- 887 + nodeStack, -- 887 + getDrawNode(enode), -- 887 + enode -- 887 + ) -- 887 + end, -- 886 + ["dot-shape"] = drawNodeCheck, -- 889 + ["segment-shape"] = drawNodeCheck, -- 890 + ["rect-shape"] = drawNodeCheck, -- 891 + ["polygon-shape"] = drawNodeCheck, -- 892 + ["verts-shape"] = drawNodeCheck, -- 893 + grid = function(nodeStack, enode, parent) -- 894 + addChild( -- 895 + nodeStack, -- 895 + getGrid(enode), -- 895 + enode -- 895 + ) -- 895 + end, -- 894 + sprite = function(nodeStack, enode, parent) -- 897 + local cnode = getSprite(enode) -- 898 + if cnode ~= nil then -- 898 + addChild(nodeStack, cnode, enode) -- 900 + end -- 900 + end, -- 897 + label = function(nodeStack, enode, parent) -- 903 + local cnode = getLabel(enode) -- 904 + if cnode ~= nil then -- 904 + addChild(nodeStack, cnode, enode) -- 906 + end -- 906 + end, -- 903 + line = function(nodeStack, enode, parent) -- 909 + addChild( -- 910 + nodeStack, -- 910 + getLine(enode), -- 910 + enode -- 910 + ) -- 910 + end, -- 909 + particle = function(nodeStack, enode, parent) -- 912 + local cnode = getParticle(enode) -- 913 + if cnode ~= nil then -- 913 + addChild(nodeStack, cnode, enode) -- 915 + end -- 915 + end, -- 912 + menu = function(nodeStack, enode, parent) -- 918 + addChild( -- 919 + nodeStack, -- 919 + getMenu(enode), -- 919 + enode -- 919 + ) -- 919 + end, -- 918 + action = function(_nodeStack, enode, parent) -- 921 + if #enode.children == 0 then -- 921 + Warn(" tag has no children") -- 923 + return -- 924 + end -- 924 + local action = enode.props -- 926 + if action.ref == nil then -- 926 + Warn(" tag has no ref") -- 928 + return -- 929 + end -- 929 + local actionStack = {} -- 931 + for i = 1, #enode.children do -- 931 + visitAction(actionStack, enode.children[i]) -- 933 + end -- 933 + if #actionStack == 1 then -- 933 + action.ref.current = actionStack[1] -- 936 + elseif #actionStack > 1 then -- 936 + action.ref.current = dora.Sequence(table.unpack(actionStack)) -- 938 + end -- 938 + end, -- 921 + ["anchor-x"] = actionCheck, -- 941 + ["anchor-y"] = actionCheck, -- 942 + angle = actionCheck, -- 943 + ["angle-x"] = actionCheck, -- 944 + ["angle-y"] = actionCheck, -- 945 + delay = actionCheck, -- 946 + event = actionCheck, -- 947 + width = actionCheck, -- 948 + height = actionCheck, -- 949 + hide = actionCheck, -- 950 + show = actionCheck, -- 951 + move = actionCheck, -- 952 + opacity = actionCheck, -- 953 + roll = actionCheck, -- 954 + scale = actionCheck, -- 955 + ["scale-x"] = actionCheck, -- 956 + ["scale-y"] = actionCheck, -- 957 + ["skew-x"] = actionCheck, -- 958 + ["skew-y"] = actionCheck, -- 959 + ["move-x"] = actionCheck, -- 960 + ["move-y"] = actionCheck, -- 961 + ["move-z"] = actionCheck, -- 962 + spawn = actionCheck, -- 963 + sequence = actionCheck, -- 964 + loop = function(nodeStack, enode, _parent) -- 965 + if #nodeStack > 0 then -- 965 + local node = nodeStack[#nodeStack] -- 967 + local actionStack = {} -- 968 + for i = 1, #enode.children do -- 968 + visitAction(actionStack, enode.children[i]) -- 970 + end -- 970 + if #actionStack == 1 then -- 970 + node:runAction(actionStack[1], true) -- 973 + else -- 973 + local loop = enode.props -- 975 + if loop.spawn then -- 975 + node:runAction( -- 977 + dora.Spawn(table.unpack(actionStack)), -- 977 + true -- 977 + ) -- 977 + else -- 977 + node:runAction( -- 979 + dora.Sequence(table.unpack(actionStack)), -- 979 + true -- 979 + ) -- 979 + end -- 979 + end -- 979 + else -- 979 + Warn("tag must be placed under a scene node to take effect") -- 983 + end -- 983 + end, -- 965 + ["physics-world"] = function(nodeStack, enode, _parent) -- 986 + addChild( -- 987 + nodeStack, -- 987 + getPhysicsWorld(enode), -- 987 + enode -- 987 + ) -- 987 + end, -- 986 + contact = function(nodeStack, enode, _parent) -- 989 + local world = dora.tolua.cast(nodeStack[#nodeStack], "PhysicsWorld") -- 990 + if world ~= nil then -- 990 + local contact = enode.props -- 992 + world:setShouldContact(contact.groupA, contact.groupB, contact.enabled) -- 993 + else -- 993 + Warn(("tag <" .. enode.type) .. "> must be placed under or its derivatives to take effect") -- 995 + end -- 995 + end, -- 989 + body = function(nodeStack, enode, _parent) -- 998 + local def = enode.props -- 999 + if def.world then -- 999 + addChild( -- 1001 + nodeStack, -- 1001 + getBody(enode, def.world), -- 1001 + enode -- 1001 + ) -- 1001 + return -- 1002 + end -- 1002 + local world = dora.tolua.cast(nodeStack[#nodeStack], "PhysicsWorld") -- 1004 + if world ~= nil then -- 1004 + addChild( -- 1006 + nodeStack, -- 1006 + getBody(enode, world), -- 1006 + enode -- 1006 + ) -- 1006 + else -- 1006 + Warn(("tag <" .. enode.type) .. "> must be placed under or its derivatives to take effect") -- 1008 + end -- 1008 + end, -- 998 + ["rect-fixture"] = bodyCheck, -- 1011 + ["polygon-fixture"] = bodyCheck, -- 1012 + ["multi-fixture"] = bodyCheck, -- 1013 + ["disk-fixture"] = bodyCheck, -- 1014 + ["chain-fixture"] = bodyCheck, -- 1015 + ["distance-joint"] = function(_nodeStack, enode, _parent) -- 1016 + local joint = enode.props -- 1017 + if joint.ref == nil then -- 1017 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1019 + return -- 1020 end -- 1020 - ____joint_ref_17.current = ____self_15_friction_16( -- 1019 - ____self_15, -- 1019 - ____joint_canCollide_14, -- 1020 - joint.bodyA.current, -- 1021 - joint.bodyB.current, -- 1022 - joint.worldPos, -- 1023 - joint.maxForce, -- 1024 - joint.maxTorque -- 1025 - ) -- 1025 - end, -- 1005 - ["gear-joint"] = function(_nodeStack, enode, _parent) -- 1028 - local joint = enode.props -- 1029 - if joint.ref == nil then -- 1029 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1031 - return -- 1032 - end -- 1032 - if joint.jointA.current == nil then -- 1032 - Warn(("not creating instance of tag <" .. enode.type) .. "> because jointA is invalid") -- 1035 - return -- 1036 - end -- 1036 - if joint.jointB.current == nil then -- 1036 - Warn(("not creating instance of tag <" .. enode.type) .. "> because jointB is invalid") -- 1039 - return -- 1040 - end -- 1040 - local ____joint_ref_21 = joint.ref -- 1042 - local ____self_19 = dora.Joint -- 1042 - local ____self_19_gear_20 = ____self_19.gear -- 1042 - local ____joint_canCollide_18 = joint.canCollide -- 1043 - if ____joint_canCollide_18 == nil then -- 1043 - ____joint_canCollide_18 = false -- 1043 + if joint.bodyA.current == nil then -- 1020 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1023 + return -- 1024 + end -- 1024 + if joint.bodyB.current == nil then -- 1024 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1027 + return -- 1028 + end -- 1028 + local ____joint_ref_13 = joint.ref -- 1030 + local ____self_11 = dora.Joint -- 1030 + local ____self_11_distance_12 = ____self_11.distance -- 1030 + local ____joint_canCollide_10 = joint.canCollide -- 1031 + if ____joint_canCollide_10 == nil then -- 1031 + ____joint_canCollide_10 = false -- 1031 + end -- 1031 + ____joint_ref_13.current = ____self_11_distance_12( -- 1030 + ____self_11, -- 1030 + ____joint_canCollide_10, -- 1031 + joint.bodyA.current, -- 1032 + joint.bodyB.current, -- 1033 + joint.anchorA or dora.Vec2.zero, -- 1034 + joint.anchorB or dora.Vec2.zero, -- 1035 + joint.frequency or 0, -- 1036 + joint.damping or 0 -- 1037 + ) -- 1037 + end, -- 1016 + ["friction-joint"] = function(_nodeStack, enode, _parent) -- 1039 + local joint = enode.props -- 1040 + if joint.ref == nil then -- 1040 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1042 + return -- 1043 end -- 1043 - ____joint_ref_21.current = ____self_19_gear_20( -- 1042 - ____self_19, -- 1042 - ____joint_canCollide_18, -- 1043 - joint.jointA.current, -- 1044 - joint.jointB.current, -- 1045 - joint.ratio or 1 -- 1046 - ) -- 1046 - end, -- 1028 - ["spring-joint"] = function(_nodeStack, enode, _parent) -- 1049 - local joint = enode.props -- 1050 - if joint.ref == nil then -- 1050 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1052 - return -- 1053 - end -- 1053 - if joint.bodyA.current == nil then -- 1053 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1056 - return -- 1057 - end -- 1057 - if joint.bodyB.current == nil then -- 1057 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1060 - return -- 1061 - end -- 1061 - local ____joint_ref_25 = joint.ref -- 1063 - local ____self_23 = dora.Joint -- 1063 - local ____self_23_spring_24 = ____self_23.spring -- 1063 - local ____joint_canCollide_22 = joint.canCollide -- 1064 - if ____joint_canCollide_22 == nil then -- 1064 - ____joint_canCollide_22 = false -- 1064 - end -- 1064 - ____joint_ref_25.current = ____self_23_spring_24( -- 1063 - ____self_23, -- 1063 - ____joint_canCollide_22, -- 1064 - joint.bodyA.current, -- 1065 - joint.bodyB.current, -- 1066 - joint.linearOffset, -- 1067 - joint.angularOffset, -- 1068 - joint.maxForce, -- 1069 - joint.maxTorque, -- 1070 - joint.correctionFactor or 1 -- 1071 - ) -- 1071 - end, -- 1049 - ["move-joint"] = function(_nodeStack, enode, _parent) -- 1074 - local joint = enode.props -- 1075 - if joint.ref == nil then -- 1075 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1077 - return -- 1078 - end -- 1078 - if joint.body.current == nil then -- 1078 - Warn(("not creating instance of tag <" .. enode.type) .. "> because body is invalid") -- 1081 - return -- 1082 - end -- 1082 - local ____joint_ref_29 = joint.ref -- 1084 - local ____self_27 = dora.Joint -- 1084 - local ____self_27_move_28 = ____self_27.move -- 1084 - local ____joint_canCollide_26 = joint.canCollide -- 1085 - if ____joint_canCollide_26 == nil then -- 1085 - ____joint_canCollide_26 = false -- 1085 - end -- 1085 - ____joint_ref_29.current = ____self_27_move_28( -- 1084 - ____self_27, -- 1084 - ____joint_canCollide_26, -- 1085 - joint.body.current, -- 1086 - joint.targetPos, -- 1087 - joint.maxForce, -- 1088 - joint.frequency, -- 1089 - joint.damping or 0.7 -- 1090 - ) -- 1090 - end, -- 1074 - ["prismatic-joint"] = function(_nodeStack, enode, _parent) -- 1093 - local joint = enode.props -- 1094 - if joint.ref == nil then -- 1094 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1096 - return -- 1097 - end -- 1097 - if joint.bodyA.current == nil then -- 1097 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1100 - return -- 1101 - end -- 1101 - if joint.bodyB.current == nil then -- 1101 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1104 - return -- 1105 - end -- 1105 - local ____joint_ref_33 = joint.ref -- 1107 - local ____self_31 = dora.Joint -- 1107 - local ____self_31_prismatic_32 = ____self_31.prismatic -- 1107 - local ____joint_canCollide_30 = joint.canCollide -- 1108 - if ____joint_canCollide_30 == nil then -- 1108 - ____joint_canCollide_30 = false -- 1108 - end -- 1108 - ____joint_ref_33.current = ____self_31_prismatic_32( -- 1107 - ____self_31, -- 1107 - ____joint_canCollide_30, -- 1108 - joint.bodyA.current, -- 1109 - joint.bodyB.current, -- 1110 - joint.worldPos, -- 1111 - joint.axisAngle, -- 1112 - joint.lowerTranslation or 0, -- 1113 - joint.upperTranslation or 0, -- 1114 - joint.maxMotorForce or 0, -- 1115 - joint.motorSpeed or 0 -- 1116 - ) -- 1116 - end, -- 1093 - ["pulley-joint"] = function(_nodeStack, enode, _parent) -- 1119 - local joint = enode.props -- 1120 - if joint.ref == nil then -- 1120 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1122 - return -- 1123 - end -- 1123 - if joint.bodyA.current == nil then -- 1123 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1126 - return -- 1127 - end -- 1127 - if joint.bodyB.current == nil then -- 1127 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1130 + if joint.bodyA.current == nil then -- 1043 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1046 + return -- 1047 + end -- 1047 + if joint.bodyB.current == nil then -- 1047 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1050 + return -- 1051 + end -- 1051 + local ____joint_ref_17 = joint.ref -- 1053 + local ____self_15 = dora.Joint -- 1053 + local ____self_15_friction_16 = ____self_15.friction -- 1053 + local ____joint_canCollide_14 = joint.canCollide -- 1054 + if ____joint_canCollide_14 == nil then -- 1054 + ____joint_canCollide_14 = false -- 1054 + end -- 1054 + ____joint_ref_17.current = ____self_15_friction_16( -- 1053 + ____self_15, -- 1053 + ____joint_canCollide_14, -- 1054 + joint.bodyA.current, -- 1055 + joint.bodyB.current, -- 1056 + joint.worldPos, -- 1057 + joint.maxForce, -- 1058 + joint.maxTorque -- 1059 + ) -- 1059 + end, -- 1039 + ["gear-joint"] = function(_nodeStack, enode, _parent) -- 1062 + local joint = enode.props -- 1063 + if joint.ref == nil then -- 1063 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1065 + return -- 1066 + end -- 1066 + if joint.jointA.current == nil then -- 1066 + Warn(("not creating instance of tag <" .. enode.type) .. "> because jointA is invalid") -- 1069 + return -- 1070 + end -- 1070 + if joint.jointB.current == nil then -- 1070 + Warn(("not creating instance of tag <" .. enode.type) .. "> because jointB is invalid") -- 1073 + return -- 1074 + end -- 1074 + local ____joint_ref_21 = joint.ref -- 1076 + local ____self_19 = dora.Joint -- 1076 + local ____self_19_gear_20 = ____self_19.gear -- 1076 + local ____joint_canCollide_18 = joint.canCollide -- 1077 + if ____joint_canCollide_18 == nil then -- 1077 + ____joint_canCollide_18 = false -- 1077 + end -- 1077 + ____joint_ref_21.current = ____self_19_gear_20( -- 1076 + ____self_19, -- 1076 + ____joint_canCollide_18, -- 1077 + joint.jointA.current, -- 1078 + joint.jointB.current, -- 1079 + joint.ratio or 1 -- 1080 + ) -- 1080 + end, -- 1062 + ["spring-joint"] = function(_nodeStack, enode, _parent) -- 1083 + local joint = enode.props -- 1084 + if joint.ref == nil then -- 1084 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1086 + return -- 1087 + end -- 1087 + if joint.bodyA.current == nil then -- 1087 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1090 + return -- 1091 + end -- 1091 + if joint.bodyB.current == nil then -- 1091 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1094 + return -- 1095 + end -- 1095 + local ____joint_ref_25 = joint.ref -- 1097 + local ____self_23 = dora.Joint -- 1097 + local ____self_23_spring_24 = ____self_23.spring -- 1097 + local ____joint_canCollide_22 = joint.canCollide -- 1098 + if ____joint_canCollide_22 == nil then -- 1098 + ____joint_canCollide_22 = false -- 1098 + end -- 1098 + ____joint_ref_25.current = ____self_23_spring_24( -- 1097 + ____self_23, -- 1097 + ____joint_canCollide_22, -- 1098 + joint.bodyA.current, -- 1099 + joint.bodyB.current, -- 1100 + joint.linearOffset, -- 1101 + joint.angularOffset, -- 1102 + joint.maxForce, -- 1103 + joint.maxTorque, -- 1104 + joint.correctionFactor or 1 -- 1105 + ) -- 1105 + end, -- 1083 + ["move-joint"] = function(_nodeStack, enode, _parent) -- 1108 + local joint = enode.props -- 1109 + if joint.ref == nil then -- 1109 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1111 + return -- 1112 + end -- 1112 + if joint.body.current == nil then -- 1112 + Warn(("not creating instance of tag <" .. enode.type) .. "> because body is invalid") -- 1115 + return -- 1116 + end -- 1116 + local ____joint_ref_29 = joint.ref -- 1118 + local ____self_27 = dora.Joint -- 1118 + local ____self_27_move_28 = ____self_27.move -- 1118 + local ____joint_canCollide_26 = joint.canCollide -- 1119 + if ____joint_canCollide_26 == nil then -- 1119 + ____joint_canCollide_26 = false -- 1119 + end -- 1119 + ____joint_ref_29.current = ____self_27_move_28( -- 1118 + ____self_27, -- 1118 + ____joint_canCollide_26, -- 1119 + joint.body.current, -- 1120 + joint.targetPos, -- 1121 + joint.maxForce, -- 1122 + joint.frequency, -- 1123 + joint.damping or 0.7 -- 1124 + ) -- 1124 + end, -- 1108 + ["prismatic-joint"] = function(_nodeStack, enode, _parent) -- 1127 + local joint = enode.props -- 1128 + if joint.ref == nil then -- 1128 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1130 return -- 1131 end -- 1131 - local ____joint_ref_37 = joint.ref -- 1133 - local ____self_35 = dora.Joint -- 1133 - local ____self_35_pulley_36 = ____self_35.pulley -- 1133 - local ____joint_canCollide_34 = joint.canCollide -- 1134 - if ____joint_canCollide_34 == nil then -- 1134 - ____joint_canCollide_34 = false -- 1134 - end -- 1134 - ____joint_ref_37.current = ____self_35_pulley_36( -- 1133 - ____self_35, -- 1133 - ____joint_canCollide_34, -- 1134 - joint.bodyA.current, -- 1135 - joint.bodyB.current, -- 1136 - joint.anchorA or dora.Vec2.zero, -- 1137 - joint.anchorB or dora.Vec2.zero, -- 1138 - joint.groundAnchorA, -- 1139 - joint.groundAnchorB, -- 1140 - joint.ratio or 1 -- 1141 - ) -- 1141 - end, -- 1119 - ["revolute-joint"] = function(_nodeStack, enode, _parent) -- 1144 - local joint = enode.props -- 1145 - if joint.ref == nil then -- 1145 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1147 - return -- 1148 - end -- 1148 - if joint.bodyA.current == nil then -- 1148 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1151 - return -- 1152 - end -- 1152 - if joint.bodyB.current == nil then -- 1152 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1155 - return -- 1156 - end -- 1156 - local ____joint_ref_41 = joint.ref -- 1158 - local ____self_39 = dora.Joint -- 1158 - local ____self_39_revolute_40 = ____self_39.revolute -- 1158 - local ____joint_canCollide_38 = joint.canCollide -- 1159 - if ____joint_canCollide_38 == nil then -- 1159 - ____joint_canCollide_38 = false -- 1159 - end -- 1159 - ____joint_ref_41.current = ____self_39_revolute_40( -- 1158 - ____self_39, -- 1158 - ____joint_canCollide_38, -- 1159 - joint.bodyA.current, -- 1160 - joint.bodyB.current, -- 1161 - joint.worldPos, -- 1162 - joint.lowerAngle or 0, -- 1163 - joint.upperAngle or 0, -- 1164 - joint.maxMotorTorque or 0, -- 1165 - joint.motorSpeed or 0 -- 1166 - ) -- 1166 - end, -- 1144 - ["rope-joint"] = function(_nodeStack, enode, _parent) -- 1169 - local joint = enode.props -- 1170 - if joint.ref == nil then -- 1170 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1172 - return -- 1173 - end -- 1173 - if joint.bodyA.current == nil then -- 1173 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1176 - return -- 1177 - end -- 1177 - if joint.bodyB.current == nil then -- 1177 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1180 - return -- 1181 - end -- 1181 - local ____joint_ref_45 = joint.ref -- 1183 - local ____self_43 = dora.Joint -- 1183 - local ____self_43_rope_44 = ____self_43.rope -- 1183 - local ____joint_canCollide_42 = joint.canCollide -- 1184 - if ____joint_canCollide_42 == nil then -- 1184 - ____joint_canCollide_42 = false -- 1184 - end -- 1184 - ____joint_ref_45.current = ____self_43_rope_44( -- 1183 - ____self_43, -- 1183 - ____joint_canCollide_42, -- 1184 - joint.bodyA.current, -- 1185 - joint.bodyB.current, -- 1186 - joint.anchorA or dora.Vec2.zero, -- 1187 - joint.anchorB or dora.Vec2.zero, -- 1188 - joint.maxLength or 0 -- 1189 - ) -- 1189 - end, -- 1169 - ["weld-joint"] = function(_nodeStack, enode, _parent) -- 1192 - local joint = enode.props -- 1193 - if joint.ref == nil then -- 1193 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1195 - return -- 1196 - end -- 1196 - if joint.bodyA.current == nil then -- 1196 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1199 - return -- 1200 - end -- 1200 - if joint.bodyB.current == nil then -- 1200 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1203 - return -- 1204 - end -- 1204 - local ____joint_ref_49 = joint.ref -- 1206 - local ____self_47 = dora.Joint -- 1206 - local ____self_47_weld_48 = ____self_47.weld -- 1206 - local ____joint_canCollide_46 = joint.canCollide -- 1207 - if ____joint_canCollide_46 == nil then -- 1207 - ____joint_canCollide_46 = false -- 1207 + if joint.bodyA.current == nil then -- 1131 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1134 + return -- 1135 + end -- 1135 + if joint.bodyB.current == nil then -- 1135 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1138 + return -- 1139 + end -- 1139 + local ____joint_ref_33 = joint.ref -- 1141 + local ____self_31 = dora.Joint -- 1141 + local ____self_31_prismatic_32 = ____self_31.prismatic -- 1141 + local ____joint_canCollide_30 = joint.canCollide -- 1142 + if ____joint_canCollide_30 == nil then -- 1142 + ____joint_canCollide_30 = false -- 1142 + end -- 1142 + ____joint_ref_33.current = ____self_31_prismatic_32( -- 1141 + ____self_31, -- 1141 + ____joint_canCollide_30, -- 1142 + joint.bodyA.current, -- 1143 + joint.bodyB.current, -- 1144 + joint.worldPos, -- 1145 + joint.axisAngle, -- 1146 + joint.lowerTranslation or 0, -- 1147 + joint.upperTranslation or 0, -- 1148 + joint.maxMotorForce or 0, -- 1149 + joint.motorSpeed or 0 -- 1150 + ) -- 1150 + end, -- 1127 + ["pulley-joint"] = function(_nodeStack, enode, _parent) -- 1153 + local joint = enode.props -- 1154 + if joint.ref == nil then -- 1154 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1156 + return -- 1157 + end -- 1157 + if joint.bodyA.current == nil then -- 1157 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1160 + return -- 1161 + end -- 1161 + if joint.bodyB.current == nil then -- 1161 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1164 + return -- 1165 + end -- 1165 + local ____joint_ref_37 = joint.ref -- 1167 + local ____self_35 = dora.Joint -- 1167 + local ____self_35_pulley_36 = ____self_35.pulley -- 1167 + local ____joint_canCollide_34 = joint.canCollide -- 1168 + if ____joint_canCollide_34 == nil then -- 1168 + ____joint_canCollide_34 = false -- 1168 + end -- 1168 + ____joint_ref_37.current = ____self_35_pulley_36( -- 1167 + ____self_35, -- 1167 + ____joint_canCollide_34, -- 1168 + joint.bodyA.current, -- 1169 + joint.bodyB.current, -- 1170 + joint.anchorA or dora.Vec2.zero, -- 1171 + joint.anchorB or dora.Vec2.zero, -- 1172 + joint.groundAnchorA, -- 1173 + joint.groundAnchorB, -- 1174 + joint.ratio or 1 -- 1175 + ) -- 1175 + end, -- 1153 + ["revolute-joint"] = function(_nodeStack, enode, _parent) -- 1178 + local joint = enode.props -- 1179 + if joint.ref == nil then -- 1179 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1181 + return -- 1182 + end -- 1182 + if joint.bodyA.current == nil then -- 1182 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1185 + return -- 1186 + end -- 1186 + if joint.bodyB.current == nil then -- 1186 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1189 + return -- 1190 + end -- 1190 + local ____joint_ref_41 = joint.ref -- 1192 + local ____self_39 = dora.Joint -- 1192 + local ____self_39_revolute_40 = ____self_39.revolute -- 1192 + local ____joint_canCollide_38 = joint.canCollide -- 1193 + if ____joint_canCollide_38 == nil then -- 1193 + ____joint_canCollide_38 = false -- 1193 + end -- 1193 + ____joint_ref_41.current = ____self_39_revolute_40( -- 1192 + ____self_39, -- 1192 + ____joint_canCollide_38, -- 1193 + joint.bodyA.current, -- 1194 + joint.bodyB.current, -- 1195 + joint.worldPos, -- 1196 + joint.lowerAngle or 0, -- 1197 + joint.upperAngle or 0, -- 1198 + joint.maxMotorTorque or 0, -- 1199 + joint.motorSpeed or 0 -- 1200 + ) -- 1200 + end, -- 1178 + ["rope-joint"] = function(_nodeStack, enode, _parent) -- 1203 + local joint = enode.props -- 1204 + if joint.ref == nil then -- 1204 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1206 + return -- 1207 end -- 1207 - ____joint_ref_49.current = ____self_47_weld_48( -- 1206 - ____self_47, -- 1206 - ____joint_canCollide_46, -- 1207 - joint.bodyA.current, -- 1208 - joint.bodyB.current, -- 1209 - joint.worldPos, -- 1210 - joint.frequency or 0, -- 1211 - joint.damping or 0 -- 1212 - ) -- 1212 - end, -- 1192 - ["wheel-joint"] = function(_nodeStack, enode, _parent) -- 1215 - local joint = enode.props -- 1216 - if joint.ref == nil then -- 1216 - Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1218 - return -- 1219 - end -- 1219 - if joint.bodyA.current == nil then -- 1219 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1222 - return -- 1223 - end -- 1223 - if joint.bodyB.current == nil then -- 1223 - Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1226 - return -- 1227 - end -- 1227 - local ____joint_ref_53 = joint.ref -- 1229 - local ____self_51 = dora.Joint -- 1229 - local ____self_51_wheel_52 = ____self_51.wheel -- 1229 - local ____joint_canCollide_50 = joint.canCollide -- 1230 - if ____joint_canCollide_50 == nil then -- 1230 - ____joint_canCollide_50 = false -- 1230 + if joint.bodyA.current == nil then -- 1207 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1210 + return -- 1211 + end -- 1211 + if joint.bodyB.current == nil then -- 1211 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1214 + return -- 1215 + end -- 1215 + local ____joint_ref_45 = joint.ref -- 1217 + local ____self_43 = dora.Joint -- 1217 + local ____self_43_rope_44 = ____self_43.rope -- 1217 + local ____joint_canCollide_42 = joint.canCollide -- 1218 + if ____joint_canCollide_42 == nil then -- 1218 + ____joint_canCollide_42 = false -- 1218 + end -- 1218 + ____joint_ref_45.current = ____self_43_rope_44( -- 1217 + ____self_43, -- 1217 + ____joint_canCollide_42, -- 1218 + joint.bodyA.current, -- 1219 + joint.bodyB.current, -- 1220 + joint.anchorA or dora.Vec2.zero, -- 1221 + joint.anchorB or dora.Vec2.zero, -- 1222 + joint.maxLength or 0 -- 1223 + ) -- 1223 + end, -- 1203 + ["weld-joint"] = function(_nodeStack, enode, _parent) -- 1226 + local joint = enode.props -- 1227 + if joint.ref == nil then -- 1227 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1229 + return -- 1230 end -- 1230 - ____joint_ref_53.current = ____self_51_wheel_52( -- 1229 - ____self_51, -- 1229 - ____joint_canCollide_50, -- 1230 - joint.bodyA.current, -- 1231 - joint.bodyB.current, -- 1232 - joint.worldPos, -- 1233 - joint.axisAngle, -- 1234 - joint.maxMotorTorque or 0, -- 1235 - joint.motorSpeed or 0, -- 1236 - joint.frequency or 0, -- 1237 - joint.damping or 0.7 -- 1238 - ) -- 1238 - end, -- 1215 - ["custom-node"] = function(nodeStack, enode, parent) -- 1241 - local node = getCustomNode(enode) -- 1242 - if node ~= nil then -- 1242 - addChild(nodeStack, node, enode) -- 1244 - end -- 1244 - end, -- 1241 - ["custom-element"] = function() -- 1247 - end -- 1247 -} -- 1247 -function ____exports.useRef(item) -- 1290 - local ____item_54 = item -- 1291 - if ____item_54 == nil then -- 1291 - ____item_54 = nil -- 1291 - end -- 1291 - return {current = ____item_54} -- 1291 -end -- 1290 -local function getPreload(preloadList, node) -- 1294 - if type(node) ~= "table" then -- 1294 - return -- 1296 - end -- 1296 - local enode = node -- 1298 - if enode.type == nil then -- 1298 - local list = node -- 1300 - if #list > 0 then -- 1300 - for i = 1, #list do -- 1300 - getPreload(preloadList, list[i]) -- 1303 - end -- 1303 - end -- 1303 - else -- 1303 - repeat -- 1303 - local ____switch281 = enode.type -- 1303 - local sprite, playable, model, spine, dragonBone, label -- 1303 - local ____cond281 = ____switch281 == "sprite" -- 1303 - if ____cond281 then -- 1303 - sprite = enode.props -- 1309 - preloadList[#preloadList + 1] = sprite.file -- 1310 - break -- 1311 - end -- 1311 - ____cond281 = ____cond281 or ____switch281 == "playable" -- 1311 - if ____cond281 then -- 1311 - playable = enode.props -- 1313 - preloadList[#preloadList + 1] = playable.file -- 1314 - break -- 1315 - end -- 1315 - ____cond281 = ____cond281 or ____switch281 == "model" -- 1315 - if ____cond281 then -- 1315 - model = enode.props -- 1317 - preloadList[#preloadList + 1] = "model:" .. model.file -- 1318 - break -- 1319 - end -- 1319 - ____cond281 = ____cond281 or ____switch281 == "spine" -- 1319 - if ____cond281 then -- 1319 - spine = enode.props -- 1321 - preloadList[#preloadList + 1] = "spine:" .. spine.file -- 1322 - break -- 1323 - end -- 1323 - ____cond281 = ____cond281 or ____switch281 == "dragon-bone" -- 1323 - if ____cond281 then -- 1323 - dragonBone = enode.props -- 1325 - preloadList[#preloadList + 1] = "bone:" .. dragonBone.file -- 1326 - break -- 1327 - end -- 1327 - ____cond281 = ____cond281 or ____switch281 == "label" -- 1327 - if ____cond281 then -- 1327 - label = enode.props -- 1329 - preloadList[#preloadList + 1] = (("font:" .. label.fontName) .. ";") .. tostring(label.fontSize) -- 1330 - break -- 1331 - end -- 1331 - until true -- 1331 - end -- 1331 - getPreload(preloadList, enode.children) -- 1334 -end -- 1294 -function ____exports.preloadAsync(enode, handler) -- 1337 - local preloadList = {} -- 1338 - getPreload(preloadList, enode) -- 1339 - dora.Cache:loadAsync(preloadList, handler) -- 1340 -end -- 1337 -return ____exports -- 1337 \ No newline at end of file + if joint.bodyA.current == nil then -- 1230 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1233 + return -- 1234 + end -- 1234 + if joint.bodyB.current == nil then -- 1234 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1237 + return -- 1238 + end -- 1238 + local ____joint_ref_49 = joint.ref -- 1240 + local ____self_47 = dora.Joint -- 1240 + local ____self_47_weld_48 = ____self_47.weld -- 1240 + local ____joint_canCollide_46 = joint.canCollide -- 1241 + if ____joint_canCollide_46 == nil then -- 1241 + ____joint_canCollide_46 = false -- 1241 + end -- 1241 + ____joint_ref_49.current = ____self_47_weld_48( -- 1240 + ____self_47, -- 1240 + ____joint_canCollide_46, -- 1241 + joint.bodyA.current, -- 1242 + joint.bodyB.current, -- 1243 + joint.worldPos, -- 1244 + joint.frequency or 0, -- 1245 + joint.damping or 0 -- 1246 + ) -- 1246 + end, -- 1226 + ["wheel-joint"] = function(_nodeStack, enode, _parent) -- 1249 + local joint = enode.props -- 1250 + if joint.ref == nil then -- 1250 + Warn(("not creating instance of tag <" .. enode.type) .. "> because it has no reference") -- 1252 + return -- 1253 + end -- 1253 + if joint.bodyA.current == nil then -- 1253 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyA is invalid") -- 1256 + return -- 1257 + end -- 1257 + if joint.bodyB.current == nil then -- 1257 + Warn(("not creating instance of tag <" .. enode.type) .. "> because bodyB is invalid") -- 1260 + return -- 1261 + end -- 1261 + local ____joint_ref_53 = joint.ref -- 1263 + local ____self_51 = dora.Joint -- 1263 + local ____self_51_wheel_52 = ____self_51.wheel -- 1263 + local ____joint_canCollide_50 = joint.canCollide -- 1264 + if ____joint_canCollide_50 == nil then -- 1264 + ____joint_canCollide_50 = false -- 1264 + end -- 1264 + ____joint_ref_53.current = ____self_51_wheel_52( -- 1263 + ____self_51, -- 1263 + ____joint_canCollide_50, -- 1264 + joint.bodyA.current, -- 1265 + joint.bodyB.current, -- 1266 + joint.worldPos, -- 1267 + joint.axisAngle, -- 1268 + joint.maxMotorTorque or 0, -- 1269 + joint.motorSpeed or 0, -- 1270 + joint.frequency or 0, -- 1271 + joint.damping or 0.7 -- 1272 + ) -- 1272 + end, -- 1249 + ["custom-node"] = function(nodeStack, enode, _parent) -- 1275 + local node = getCustomNode(enode) -- 1276 + if node ~= nil then -- 1276 + addChild(nodeStack, node, enode) -- 1278 + end -- 1278 + end, -- 1275 + ["custom-element"] = function() -- 1281 + end, -- 1281 + ["align-node"] = function(nodeStack, enode, _parent) -- 1282 + addChild( -- 1283 + nodeStack, -- 1283 + getAlignNode(enode), -- 1283 + enode -- 1283 + ) -- 1283 + end -- 1282 +} -- 1282 +function ____exports.useRef(item) -- 1327 + local ____item_54 = item -- 1328 + if ____item_54 == nil then -- 1328 + ____item_54 = nil -- 1328 + end -- 1328 + return {current = ____item_54} -- 1328 +end -- 1327 +local function getPreload(preloadList, node) -- 1331 + if type(node) ~= "table" then -- 1331 + return -- 1333 + end -- 1333 + local enode = node -- 1335 + if enode.type == nil then -- 1335 + local list = node -- 1337 + if #list > 0 then -- 1337 + for i = 1, #list do -- 1337 + getPreload(preloadList, list[i]) -- 1340 + end -- 1340 + end -- 1340 + else -- 1340 + repeat -- 1340 + local ____switch291 = enode.type -- 1340 + local sprite, playable, model, spine, dragonBone, label -- 1340 + local ____cond291 = ____switch291 == "sprite" -- 1340 + if ____cond291 then -- 1340 + sprite = enode.props -- 1346 + preloadList[#preloadList + 1] = sprite.file -- 1347 + break -- 1348 + end -- 1348 + ____cond291 = ____cond291 or ____switch291 == "playable" -- 1348 + if ____cond291 then -- 1348 + playable = enode.props -- 1350 + preloadList[#preloadList + 1] = playable.file -- 1351 + break -- 1352 + end -- 1352 + ____cond291 = ____cond291 or ____switch291 == "model" -- 1352 + if ____cond291 then -- 1352 + model = enode.props -- 1354 + preloadList[#preloadList + 1] = "model:" .. model.file -- 1355 + break -- 1356 + end -- 1356 + ____cond291 = ____cond291 or ____switch291 == "spine" -- 1356 + if ____cond291 then -- 1356 + spine = enode.props -- 1358 + preloadList[#preloadList + 1] = "spine:" .. spine.file -- 1359 + break -- 1360 + end -- 1360 + ____cond291 = ____cond291 or ____switch291 == "dragon-bone" -- 1360 + if ____cond291 then -- 1360 + dragonBone = enode.props -- 1362 + preloadList[#preloadList + 1] = "bone:" .. dragonBone.file -- 1363 + break -- 1364 + end -- 1364 + ____cond291 = ____cond291 or ____switch291 == "label" -- 1364 + if ____cond291 then -- 1364 + label = enode.props -- 1366 + preloadList[#preloadList + 1] = (("font:" .. label.fontName) .. ";") .. tostring(label.fontSize) -- 1367 + break -- 1368 + end -- 1368 + until true -- 1368 + end -- 1368 + getPreload(preloadList, enode.children) -- 1371 +end -- 1331 +function ____exports.preloadAsync(enode, handler) -- 1374 + local preloadList = {} -- 1375 + getPreload(preloadList, enode) -- 1376 + dora.Cache:loadAsync(preloadList, handler) -- 1377 +end -- 1374 +return ____exports -- 1374 \ No newline at end of file diff --git a/Assets/Script/Test/LayoutTS.lua b/Assets/Script/Test/LayoutTS.lua new file mode 100644 index 000000000..3b388fe36 --- /dev/null +++ b/Assets/Script/Test/LayoutTS.lua @@ -0,0 +1,17 @@ +-- [ts]: LayoutTS.ts +local ____exports = {} -- 1 +local ____dora = require("dora") -- 1 +local AlignNode = ____dora.AlignNode -- 1 +local root = AlignNode(true) -- 3 +root.showDebug = true -- 4 +local node1 = AlignNode() -- 6 +node1:css("\n\theight: 250;\n\tmargin: 10;\n\tpadding: 10;\n\talign-items: flex-start;\n\tflex-wrap: wrap;\n") -- 7 +node1.showDebug = true -- 14 +node1:addTo(root) -- 15 +for _ = 1, 10 do -- 15 + local node = AlignNode() -- 18 + node:css("margin: 5; height: 50; width: 50;") -- 19 + node.showDebug = true -- 20 + node:addTo(node1) -- 21 +end -- 21 +return ____exports -- 21 \ No newline at end of file diff --git a/Assets/Script/Test/LayoutTS.ts b/Assets/Script/Test/LayoutTS.ts new file mode 100644 index 000000000..d11c92bb1 --- /dev/null +++ b/Assets/Script/Test/LayoutTS.ts @@ -0,0 +1,22 @@ +import {AlignNode} from 'dora'; + +const root = AlignNode(true); +root.showDebug = true; + +const node1 = AlignNode(); +node1.css(` + height: 250; + margin: 10; + padding: 10; + align-items: flex-start; + flex-wrap: wrap; +`); +node1.showDebug = true; +node1.addTo(root); + +for (let _ of $range(1, 10)) { + const node = AlignNode(); + node.css(`margin: 5; height: 50; width: 50;`); + node.showDebug = true; + node.addTo(node1); +} diff --git a/Assets/Script/Test/LayoutTSX.lua b/Assets/Script/Test/LayoutTSX.lua new file mode 100644 index 000000000..4e3e1e13f --- /dev/null +++ b/Assets/Script/Test/LayoutTSX.lua @@ -0,0 +1,236 @@ +-- [tsx]: LayoutTSX.tsx +local ____lualib = require("lualib_bundle") -- 1 +local __TS__ArrayMap = ____lualib.__TS__ArrayMap -- 1 +local ____exports = {} -- 1 +local ____dora_2Dx = require("dora-x") -- 2 +local React = ____dora_2Dx.React -- 2 +local toNode = ____dora_2Dx.toNode -- 2 +local ____dora = require("dora") -- 3 +local App = ____dora.App -- 3 +local Vec2 = ____dora.Vec2 -- 3 +local threadLoop = ____dora.threadLoop -- 3 +local ImGui = require("ImGui") -- 5 +local current = nil -- 7 +local function Test(name, jsx) -- 9 + return { -- 10 + name = name, -- 10 + test = function() -- 10 + current = toNode(React:createElement("align-node", {windowRoot = true, style = {padding = 10, flexDirection = "row"}}, jsx)) -- 11 + end -- 10 + } -- 10 +end -- 9 +local tests = { -- 19 + Test( -- 21 + "Align Content", -- 21 + React:createElement( -- 21 + "align-node", -- 21 + {showDebug = true, style = { -- 21 + width = 200, -- 24 + height = 250, -- 25 + padding = 10, -- 26 + alignContent = "flex-start", -- 27 + flexWrap = "wrap" -- 28 + }}, -- 28 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}), -- 28 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}), -- 28 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}), -- 28 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}) -- 28 + ) -- 28 + ), -- 28 + Test( -- 37 + "Align Items", -- 37 + React:createElement( -- 37 + "align-node", -- 37 + {showDebug = true, style = {width = 200, height = 250, padding = 10, alignItems = "flex-start"}}, -- 37 + React:createElement("align-node", {showDebug = true, style = {margin = 5, height = 50, width = 50, alignSelf = "center"}}), -- 37 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}) -- 37 + ) -- 37 + ), -- 37 + Test( -- 57 + "Aspect Ratio", -- 57 + React:createElement( -- 57 + "align-node", -- 57 + {showDebug = true, style = {width = 200, height = 200, padding = 10}}, -- 57 + React:createElement("align-node", {style = {margin = 5, height = 50, aspectRatio = 1}, showDebug = true}), -- 57 + React:createElement("align-node", {style = {margin = 5, height = 50, aspectRatio = 1.5}, showDebug = true}) -- 57 + ) -- 57 + ), -- 57 + Test( -- 69 + "Display", -- 69 + React:createElement( -- 69 + "align-node", -- 69 + {showDebug = true, style = {width = 200, height = 200, padding = 10}}, -- 69 + React:createElement("align-node", {style = {margin = 5, height = 50, display = "none"}, showDebug = true}), -- 69 + React:createElement("align-node", {style = {margin = 5, height = 50, display = "flex"}, showDebug = true}) -- 69 + ) -- 69 + ), -- 69 + Test( -- 81 + "Flex Basis, Grow, and Shrink", -- 81 + React:createElement( -- 81 + React.Fragment, -- 81 + nil, -- 81 + React:createElement( -- 81 + "align-node", -- 81 + {showDebug = true, style = {width = 200, height = 200, padding = 10}}, -- 81 + React:createElement("align-node", {style = {margin = 5, flexBasis = 50}, showDebug = true}) -- 81 + ), -- 81 + React:createElement( -- 81 + "align-node", -- 81 + {showDebug = true, style = {width = 200, height = 200, padding = 10}}, -- 81 + React:createElement("align-node", {style = {margin = 5, flexGrow = 0.25}, showDebug = true}), -- 81 + React:createElement("align-node", {style = {margin = 5, flexGrow = 0.75}, showDebug = true}) -- 81 + ), -- 81 + React:createElement( -- 81 + "align-node", -- 81 + {showDebug = true, style = {width = 200, height = 200, padding = 10}}, -- 81 + React:createElement("align-node", {style = {margin = 5, flexShrink = 5, height = 150}, showDebug = true}), -- 81 + React:createElement("align-node", {style = {margin = 5, flexShrink = 10, height = 150}, showDebug = true}) -- 81 + ) -- 81 + ) -- 81 + ), -- 81 + Test( -- 114 + "Flex Direction", -- 114 + React:createElement( -- 114 + "align-node", -- 114 + {showDebug = true, style = {width = 200, height = 200, padding = 10, flexDirection = "column"}}, -- 114 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}), -- 114 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}) -- 114 + ) -- 114 + ), -- 114 + Test( -- 127 + "Flex Wrap", -- 127 + React:createElement( -- 127 + "align-node", -- 127 + {showDebug = true, style = {width = 200, height = 150, padding = 10, flexWrap = "wrap"}}, -- 127 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}), -- 127 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}), -- 127 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}), -- 127 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}) -- 127 + ) -- 127 + ), -- 127 + Test( -- 142 + "Gap", -- 142 + React:createElement( -- 142 + "align-node", -- 142 + {showDebug = true, style = { -- 142 + width = 200, -- 145 + height = 250, -- 146 + padding = 10, -- 147 + flexWrap = "wrap", -- 148 + gap = 10 -- 149 + }}, -- 149 + React:createElement("align-node", {style = {height = 50, width = 50}, showDebug = true}), -- 149 + React:createElement("align-node", {style = {height = 50, width = 50}, showDebug = true}), -- 149 + React:createElement("align-node", {style = {height = 50, width = 50}, showDebug = true}), -- 149 + React:createElement("align-node", {style = {height = 50, width = 50}, showDebug = true}), -- 149 + React:createElement("align-node", {style = {height = 50, width = 50}, showDebug = true}) -- 149 + ) -- 149 + ), -- 149 + Test( -- 159 + "Insets", -- 159 + React:createElement( -- 159 + "align-node", -- 159 + {showDebug = true, style = {width = 200, height = 200}}, -- 159 + React:createElement("align-node", {showDebug = true, style = {height = 50, width = 50, top = 50, left = 50}}) -- 159 + ) -- 159 + ), -- 159 + Test( -- 176 + "Justify Content", -- 176 + React:createElement( -- 176 + "align-node", -- 176 + {showDebug = true, style = {width = 200, height = 200, padding = 10, justifyContent = "flex-end"}}, -- 176 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}), -- 176 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}) -- 176 + ) -- 176 + ), -- 176 + Test( -- 189 + "Layout Direction", -- 189 + React:createElement( -- 189 + "align-node", -- 189 + {showDebug = true, style = {width = 200, height = 200, padding = 10, direction = "rtl"}}, -- 189 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}), -- 189 + React:createElement("align-node", {style = {margin = 5, height = 50, width = 50}, showDebug = true}) -- 189 + ) -- 189 + ), -- 189 + Test( -- 202 + "Margin, Padding, and Border", -- 202 + React:createElement( -- 202 + "align-node", -- 202 + {showDebug = true, style = {width = 200, height = 200, padding = 10}}, -- 202 + React:createElement("align-node", {showDebug = true, style = {margin = 5, padding = 20, border = 20, height = 50}}), -- 202 + React:createElement("align-node", {style = {height = 50}, showDebug = true}) -- 202 + ) -- 202 + ), -- 202 + Test( -- 221 + "Position", -- 221 + React:createElement( -- 221 + "align-node", -- 221 + {showDebug = true, style = {width = 200, height = 200, padding = 10}}, -- 221 + React:createElement("align-node", {showDebug = true, style = {margin = 5, height = 50, top = 20, position = "relative"}}) -- 221 + ) -- 221 + ), -- 221 + Test( -- 239 + "Min/Max Width and Height", -- 239 + React:createElement( -- 239 + "align-node", -- 239 + {showDebug = true, style = {width = 200, height = 250, margin = 20, padding = 10}}, -- 239 + React:createElement("align-node", {style = {margin = 5, height = 25}, showDebug = true}), -- 239 + React:createElement("align-node", {showDebug = true, style = {margin = 5, height = 100, maxHeight = 25}}), -- 239 + React:createElement("align-node", {showDebug = true, style = {margin = 5, height = 25, minHeight = 50}}), -- 239 + React:createElement("align-node", {showDebug = true, style = {margin = 5, height = 25, maxWidth = 25}}), -- 239 + React:createElement("align-node", {showDebug = true, style = {margin = 5, height = 25, width = 25, minWidth = 50}}) -- 239 + ) -- 239 + ), -- 239 + Test( -- 280 + "Width and Height", -- 280 + React:createElement( -- 280 + "align-node", -- 280 + {showDebug = true, style = {width = 200, height = 200, padding = 10}}, -- 280 + React:createElement("align-node", {showDebug = true, style = {margin = 5, height = "50%", width = "65%"}}) -- 280 + ) -- 280 + ) -- 280 +} -- 280 +tests[1]:test() -- 298 +local testNames = __TS__ArrayMap( -- 300 + tests, -- 300 + function(____, t) return t.name end -- 300 +) -- 300 +local currentTest = 1 -- 302 +local windowFlags = { -- 303 + "NoDecoration", -- 304 + "NoSavedSettings", -- 305 + "NoFocusOnAppearing", -- 306 + "NoNav", -- 307 + "NoMove" -- 308 +} -- 308 +threadLoop(function() -- 310 + local ____App_visualSize_0 = App.visualSize -- 311 + local width = ____App_visualSize_0.width -- 311 + ImGui.SetNextWindowPos( -- 312 + Vec2(width - 10, 10), -- 312 + "Always", -- 312 + Vec2(1, 0) -- 312 + ) -- 312 + ImGui.SetNextWindowSize( -- 313 + Vec2(200, 0), -- 313 + "Always" -- 313 + ) -- 313 + ImGui.Begin( -- 314 + "Layout", -- 314 + windowFlags, -- 314 + function() -- 314 + ImGui.Text("Layout (TSX)") -- 315 + ImGui.Separator() -- 316 + local changed = false -- 317 + changed, currentTest = ImGui.Combo("Test", currentTest, testNames) -- 318 + if changed then -- 318 + if current then -- 318 + current:removeFromParent() -- 321 + end -- 321 + tests[currentTest]:test() -- 323 + end -- 323 + end -- 314 + ) -- 314 + return false -- 326 +end) -- 310 +return ____exports -- 310 \ No newline at end of file diff --git a/Assets/Script/Test/LayoutTSX.tsx b/Assets/Script/Test/LayoutTSX.tsx new file mode 100644 index 000000000..dd6c4d397 --- /dev/null +++ b/Assets/Script/Test/LayoutTSX.tsx @@ -0,0 +1,327 @@ +// @preview-file on +import { React, toNode } from 'dora-x'; +import { App, Vec2, threadLoop, Node } from 'dora'; +import { SetCond, WindowFlag } from 'ImGui'; +import * as ImGui from 'ImGui'; + +let current: Node.Type | null = null; + +function Test(this: void, name: string, jsx: React.Element) { + return {name, test: () => { + current = toNode( + + {jsx} + + ); + }}; +} + +const tests = [ + + Test("Align Content", + + + + + + + ), + + Test("Align Items", + + + + + ), + + Test("Aspect Ratio", + + + + + ), + + Test("Display", + + + + + ), + + Test("Flex Basis, Grow, and Shrink", + <> + + + + + + + + + + + + + + + ), + + Test("Flex Direction", + + + + + ), + + Test("Flex Wrap", + + + + + + + ), + + Test("Gap", + + + + + + + + ), + + Test("Insets", + + + + ), + + Test("Justify Content", + + + + + ), + + Test("Layout Direction", + + + + + ), + + Test("Margin, Padding, and Border", + + + + + ), + + Test("Position", + + + + ), + + Test("Min/Max Width and Height", + + + + + + + + ), + + Test("Width and Height", + + + + ) +]; + +tests[0].test(); + +const testNames = tests.map(t => t.name); + +let currentTest = 1; +const windowFlags = [ + WindowFlag.NoDecoration, + WindowFlag.NoSavedSettings, + WindowFlag.NoFocusOnAppearing, + WindowFlag.NoNav, + WindowFlag.NoMove +]; +threadLoop(() => { + const {width} = App.visualSize; + ImGui.SetNextWindowPos(Vec2(width - 10, 10), SetCond.Always, Vec2(1, 0)); + ImGui.SetNextWindowSize(Vec2(200, 0), SetCond.Always); + ImGui.Begin("Layout", windowFlags, () => { + ImGui.Text("Layout (TSX)"); + ImGui.Separator(); + let changed = false; + [changed, currentTest] = ImGui.Combo("Test", currentTest, testNames); + if (changed) { + if (current) { + current.removeFromParent(); + } + tests[currentTest - 1].test(); + } + }); + return false; +}); \ No newline at end of file diff --git a/Assets/Script/Test/SpriteTL.lua b/Assets/Script/Test/SpriteTL.lua index 57a9c5ff1..dc12d2134 100644 --- a/Assets/Script/Test/SpriteTL.lua +++ b/Assets/Script/Test/SpriteTL.lua @@ -5,6 +5,7 @@ local sprite = Sprite("Image/logo.png") if sprite == nil then return end +sprite.showDebug = true sprite.scaleX = 0.5 sprite.scaleY = 0.5 sprite.touchEnabled = true @@ -106,7 +107,12 @@ threadLoop(function() local parent = sprite.parent parent:removeChild(sprite) sprite = Sprite("Image/logo.png") - parent:addChild(sprite) + if not (sprite == nil) then + sprite.scaleX = 0.5 + sprite.scaleY = 0.5 + sprite.showDebug = true + parent:addChild(sprite) + end end end) end) \ No newline at end of file diff --git a/Assets/Script/Test/SpriteTL.tl b/Assets/Script/Test/SpriteTL.tl index aee132abc..aad9aad10 100644 --- a/Assets/Script/Test/SpriteTL.tl +++ b/Assets/Script/Test/SpriteTL.tl @@ -5,6 +5,7 @@ local sprite = Sprite("Image/logo.png") if sprite is nil then return end +sprite.showDebug = true sprite.scaleX = 0.5 sprite.scaleY = 0.5 sprite.touchEnabled = true @@ -106,7 +107,12 @@ threadLoop(function(): boolean local parent = sprite.parent parent:removeChild(sprite) sprite = Sprite("Image/logo.png") - parent:addChild(sprite) + if not sprite is nil then + sprite.scaleX = 0.5 + sprite.scaleY = 0.5 + sprite.showDebug = true + parent:addChild(sprite) + end end end) end) diff --git a/LICENSES.3rdparty.md b/LICENSES.3rdparty.md index 6d4946d5a..2603ab6ce 100644 --- a/LICENSES.3rdparty.md +++ b/LICENSES.3rdparty.md @@ -52,6 +52,7 @@ THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL - [deno_std](https://github.com/denoland/deno_std): [MIT License](Tools/dora-dora/src/3rdParty/LICENSE-deno) - [monaco-editor-auto-typings](https://github.com/lukasbach/monaco-editor-auto-typings): [MIT License](Tools/dora-dora/src/3rdParty/monaco-editor-auto-typings/LICENSE) - [TypeScriptToLua](https://github.com/TypeScriptToLua/TypeScriptToLua): [MIT License](Tools/dora-dora/src/3rdParty/tstl/LICENSE) +- [yoga](https://github.com/facebook/yoga): [MIT License](Source/3rdParty/yoga/LICENSE) diff --git a/Project/Android/Dora/app/CMakeLists.txt b/Project/Android/Dora/app/CMakeLists.txt index d99a00a05..4b5a4e4d6 100644 --- a/Project/Android/Dora/app/CMakeLists.txt +++ b/Project/Android/Dora/app/CMakeLists.txt @@ -156,6 +156,7 @@ add_library(main SHARED src/main/cpp/Node/Playable.cpp src/main/cpp/Node/DragonBone.cpp src/main/cpp/Node/DrawNode.cpp + src/main/cpp/Node/AlignNode.cpp src/main/cpp/Event/EventType.cpp src/main/cpp/Event/EventQueue.cpp src/main/cpp/Event/Listener.cpp @@ -493,6 +494,25 @@ add_library(main SHARED src/main/cpp/3rdParty/zlib/gzclose.c src/main/cpp/3rdParty/zlib/inffast.c src/main/cpp/3rdParty/zlib/adler32.c + src/main/cpp/3rdParty/yoga/YGNodeStyle.cpp + src/main/cpp/3rdParty/yoga/YGPixelGrid.cpp + src/main/cpp/3rdParty/yoga/YGEnums.cpp + src/main/cpp/3rdParty/yoga/YGValue.cpp + src/main/cpp/3rdParty/yoga/YGConfig.cpp + src/main/cpp/3rdParty/yoga/YGNode.cpp + src/main/cpp/3rdParty/yoga/YGNodeLayout.cpp + src/main/cpp/3rdParty/yoga/config/Config.cpp + src/main/cpp/3rdParty/yoga/algorithm/AbsoluteLayout.cpp + src/main/cpp/3rdParty/yoga/algorithm/CalculateLayout.cpp + src/main/cpp/3rdParty/yoga/algorithm/PixelGrid.cpp + src/main/cpp/3rdParty/yoga/algorithm/Cache.cpp + src/main/cpp/3rdParty/yoga/algorithm/Baseline.cpp + src/main/cpp/3rdParty/yoga/algorithm/FlexLine.cpp + src/main/cpp/3rdParty/yoga/node/LayoutResults.cpp + src/main/cpp/3rdParty/yoga/node/Node.cpp + src/main/cpp/3rdParty/yoga/event/event.cpp + src/main/cpp/3rdParty/yoga/debug/Log.cpp + src/main/cpp/3rdParty/yoga/debug/AssertFatal.cpp ) target_link_libraries(main android log EGL z GLESv1_CM GLESv2 SDL2-lib BGFX-lib BIMGDE-lib BIMG-lib BX-lib CRYPTO-lib SSL-lib) diff --git a/Project/Android/Dora/app/build.gradle b/Project/Android/Dora/app/build.gradle index 45cbf25d8..a9fc804aa 100644 --- a/Project/Android/Dora/app/build.gradle +++ b/Project/Android/Dora/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "org.ippclub.dorassr" minSdkVersion 28 targetSdkVersion 34 - versionCode 54 - versionName "1.3.22" + versionCode 55 + versionName "1.3.23" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' externalNativeBuild { cmake { diff --git a/Project/Linux/CMakeLists.txt b/Project/Linux/CMakeLists.txt index c35ec0c99..3fd65f846 100644 --- a/Project/Linux/CMakeLists.txt +++ b/Project/Linux/CMakeLists.txt @@ -148,6 +148,7 @@ add_executable(dora-ssr ../../Source/Node/Playable.cpp ../../Source/Node/DragonBone.cpp ../../Source/Node/DrawNode.cpp + ../../Source/Node/AlignNode.cpp ../../Source/Event/EventType.cpp ../../Source/Event/EventQueue.cpp ../../Source/Event/Listener.cpp @@ -485,6 +486,25 @@ add_executable(dora-ssr ../../Source/3rdParty/zlib/gzclose.c ../../Source/3rdParty/zlib/inffast.c ../../Source/3rdParty/zlib/adler32.c + ../../Source/3rdParty/yoga/YGNodeStyle.cpp + ../../Source/3rdParty/yoga/YGPixelGrid.cpp + ../../Source/3rdParty/yoga/YGEnums.cpp + ../../Source/3rdParty/yoga/YGValue.cpp + ../../Source/3rdParty/yoga/YGConfig.cpp + ../../Source/3rdParty/yoga/YGNode.cpp + ../../Source/3rdParty/yoga/YGNodeLayout.cpp + ../../Source/3rdParty/yoga/config/Config.cpp + ../../Source/3rdParty/yoga/algorithm/AbsoluteLayout.cpp + ../../Source/3rdParty/yoga/algorithm/CalculateLayout.cpp + ../../Source/3rdParty/yoga/algorithm/PixelGrid.cpp + ../../Source/3rdParty/yoga/algorithm/Cache.cpp + ../../Source/3rdParty/yoga/algorithm/Baseline.cpp + ../../Source/3rdParty/yoga/algorithm/FlexLine.cpp + ../../Source/3rdParty/yoga/node/LayoutResults.cpp + ../../Source/3rdParty/yoga/node/Node.cpp + ../../Source/3rdParty/yoga/event/event.cpp + ../../Source/3rdParty/yoga/debug/Log.cpp + ../../Source/3rdParty/yoga/debug/AssertFatal.cpp ) target_link_libraries(dora-ssr z dl pthread rt ${SDL2_LIBRARIES} BGFX-lib BIMGDE-lib BIMG-lib BX-lib GL X11 OpenSSL::SSL OpenSSL::Crypto) diff --git a/Project/Windows/Dora/Dora.vcxproj b/Project/Windows/Dora/Dora.vcxproj index 1d3f6101a..bf6447d51 100644 --- a/Project/Windows/Dora/Dora.vcxproj +++ b/Project/Windows/Dora/Dora.vcxproj @@ -1181,6 +1181,82 @@ NotUsing NotUsing + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing NotUsing @@ -1333,6 +1409,7 @@ + @@ -1818,6 +1895,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1901,6 +2031,7 @@ + diff --git a/Project/Windows/Dora/Dora.vcxproj.filters b/Project/Windows/Dora/Dora.vcxproj.filters index 4d4f39360..f44a0705c 100644 --- a/Project/Windows/Dora/Dora.vcxproj.filters +++ b/Project/Windows/Dora/Dora.vcxproj.filters @@ -229,6 +229,33 @@ {df4b410f-b909-4d7a-b7b8-7f7d2643ca81} + + {584d53c5-9bd1-41e1-bfd7-4a4500917b9d} + + + {58858286-84a8-4f3f-b4a4-3a4528465c10} + + + {96d52929-491b-4b5c-af25-c8f8203cd792} + + + {59c9ebe4-f60d-4057-b39f-6d80fc18ecc5} + + + {040c966d-05ee-4582-ab4f-32cf6cca2a8f} + + + {cf6fb3a3-57a6-4bbb-8f80-764efdc708ad} + + + {ef2fd136-2ce9-40df-90c0-e9c7079ce4d5} + + + {20d9c919-41c9-40a7-88b1-cbfc2f263609} + + + {f935156f-441f-4947-917c-11a198f6d7be} + @@ -1497,6 +1524,66 @@ 3rdParty\zlib + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\config + + + 3rdParty\yoga\debug + + + 3rdParty\yoga\debug + + + 3rdParty\yoga\event + + + 3rdParty\yoga\node + + + 3rdParty\yoga\node + + + Node + @@ -3165,6 +3252,168 @@ 3rdParty\zlib + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\algorithm + + + 3rdParty\yoga\config + + + 3rdParty\yoga\debug + + + 3rdParty\yoga\debug + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\enums + + + 3rdParty\yoga\event + + + 3rdParty\yoga\style + + + 3rdParty\yoga\style + + + 3rdParty\yoga\style + + + 3rdParty\yoga\style + + + 3rdParty\yoga\style + + + 3rdParty\yoga\numeric + + + 3rdParty\yoga\numeric + + + 3rdParty\yoga\node + + + 3rdParty\yoga\node + + + 3rdParty\yoga\node + + + Node + diff --git a/Project/Windows/Dora/Resource.rc b/Project/Windows/Dora/Resource.rc index f4b151adf..a3dbce241 100644 --- a/Project/Windows/Dora/Resource.rc +++ b/Project/Windows/Dora/Resource.rc @@ -61,8 +61,8 @@ IDI_ICON1 ICON "Dora SSR.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,3,22,0 - PRODUCTVERSION 1,3,22,0 + FILEVERSION 1,3,23,0 + PRODUCTVERSION 1,3,23,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -79,12 +79,12 @@ BEGIN BEGIN VALUE "CompanyName", "IppClub" VALUE "FileDescription", "A game engine for rapid game development." - VALUE "FileVersion", "1.3.22.0" + VALUE "FileVersion", "1.3.23.0" VALUE "InternalName", "Dora.exe" VALUE "LegalCopyright", "Copyright (C) 2024" VALUE "OriginalFilename", "Dora.exe" VALUE "ProductName", "Dora SSR" - VALUE "ProductVersion", "1.3.22.0" + VALUE "ProductVersion", "1.3.23.0" END END BLOCK "VarFileInfo" diff --git a/Project/iOS/Dora.xcodeproj/project.pbxproj b/Project/iOS/Dora.xcodeproj/project.pbxproj index d783a21e5..5a4a8892b 100644 --- a/Project/iOS/Dora.xcodeproj/project.pbxproj +++ b/Project/iOS/Dora.xcodeproj/project.pbxproj @@ -159,6 +159,46 @@ 3C5399F72998DEF800E9125C /* ljson.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C5399F52998DEF800E9125C /* ljson.c */; }; 3C5399FB2998DF4900E9125C /* HttpServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5399F92998DF4900E9125C /* HttpServer.cpp */; }; 3C5399FC2998DF4900E9125C /* HttpServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5399F92998DF4900E9125C /* HttpServer.cpp */; }; + 3C53E9DA2BEB442300039640 /* YGPixelGrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9CA2BEB442200039640 /* YGPixelGrid.cpp */; }; + 3C53E9DB2BEB442300039640 /* YGPixelGrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9CA2BEB442200039640 /* YGPixelGrid.cpp */; }; + 3C53E9DC2BEB442300039640 /* YGConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9CD2BEB442200039640 /* YGConfig.cpp */; }; + 3C53E9DD2BEB442300039640 /* YGConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9CD2BEB442200039640 /* YGConfig.cpp */; }; + 3C53E9DE2BEB442300039640 /* YGValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D42BEB442300039640 /* YGValue.cpp */; }; + 3C53E9DF2BEB442300039640 /* YGValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D42BEB442300039640 /* YGValue.cpp */; }; + 3C53E9E02BEB442300039640 /* YGNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D62BEB442300039640 /* YGNode.cpp */; }; + 3C53E9E12BEB442300039640 /* YGNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D62BEB442300039640 /* YGNode.cpp */; }; + 3C53E9E22BEB442300039640 /* YGNodeStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D72BEB442300039640 /* YGNodeStyle.cpp */; }; + 3C53E9E32BEB442300039640 /* YGNodeStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D72BEB442300039640 /* YGNodeStyle.cpp */; }; + 3C53E9E42BEB442300039640 /* YGNodeLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D82BEB442300039640 /* YGNodeLayout.cpp */; }; + 3C53E9E52BEB442300039640 /* YGNodeLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D82BEB442300039640 /* YGNodeLayout.cpp */; }; + 3C53E9E62BEB442300039640 /* YGEnums.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D92BEB442300039640 /* YGEnums.cpp */; }; + 3C53E9E72BEB442300039640 /* YGEnums.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9D92BEB442300039640 /* YGEnums.cpp */; }; + 3C53E9FC2BEB44B000039640 /* Node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9F72BEB44B000039640 /* Node.cpp */; }; + 3C53E9FD2BEB44B000039640 /* Node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9F72BEB44B000039640 /* Node.cpp */; }; + 3C53E9FE2BEB44B000039640 /* LayoutResults.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9F82BEB44B000039640 /* LayoutResults.cpp */; }; + 3C53E9FF2BEB44B000039640 /* LayoutResults.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9F82BEB44B000039640 /* LayoutResults.cpp */; }; + 3C53EA022BEB44C100039640 /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA002BEB44C100039640 /* event.cpp */; }; + 3C53EA032BEB44C100039640 /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA002BEB44C100039640 /* event.cpp */; }; + 3C53EA1B2BEB44E800039640 /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA182BEB44E800039640 /* Log.cpp */; }; + 3C53EA1C2BEB44E800039640 /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA182BEB44E800039640 /* Log.cpp */; }; + 3C53EA1D2BEB44E800039640 /* AssertFatal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA1A2BEB44E800039640 /* AssertFatal.cpp */; }; + 3C53EA1E2BEB44E800039640 /* AssertFatal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA1A2BEB44E800039640 /* AssertFatal.cpp */; }; + 3C53EA212BEB450000039640 /* Config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA202BEB450000039640 /* Config.cpp */; }; + 3C53EA222BEB450000039640 /* Config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA202BEB450000039640 /* Config.cpp */; }; + 3C53EA342BEB451A00039640 /* Baseline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA242BEB451A00039640 /* Baseline.cpp */; }; + 3C53EA352BEB451A00039640 /* Baseline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA242BEB451A00039640 /* Baseline.cpp */; }; + 3C53EA362BEB451A00039640 /* CalculateLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA252BEB451A00039640 /* CalculateLayout.cpp */; }; + 3C53EA372BEB451A00039640 /* CalculateLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA252BEB451A00039640 /* CalculateLayout.cpp */; }; + 3C53EA382BEB451A00039640 /* PixelGrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA262BEB451A00039640 /* PixelGrid.cpp */; }; + 3C53EA392BEB451A00039640 /* PixelGrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA262BEB451A00039640 /* PixelGrid.cpp */; }; + 3C53EA3A2BEB451A00039640 /* FlexLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA282BEB451A00039640 /* FlexLine.cpp */; }; + 3C53EA3B2BEB451A00039640 /* FlexLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA282BEB451A00039640 /* FlexLine.cpp */; }; + 3C53EA3C2BEB451A00039640 /* Cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA292BEB451A00039640 /* Cache.cpp */; }; + 3C53EA3D2BEB451A00039640 /* Cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA292BEB451A00039640 /* Cache.cpp */; }; + 3C53EA3E2BEB451A00039640 /* AbsoluteLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA312BEB451A00039640 /* AbsoluteLayout.cpp */; }; + 3C53EA3F2BEB451A00039640 /* AbsoluteLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA312BEB451A00039640 /* AbsoluteLayout.cpp */; }; + 3C53EA422BEB456700039640 /* AlignNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA412BEB456700039640 /* AlignNode.cpp */; }; + 3C53EA432BEB456700039640 /* AlignNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53EA412BEB456700039640 /* AlignNode.cpp */; }; 3C5972FE1E7F74CB00BFD00F /* Animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5972F81E7F74CB00BFD00F /* Animation.cpp */; }; 3C5973001E7F74CB00BFD00F /* ModelDef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5972FC1E7F74CB00BFD00F /* ModelDef.cpp */; }; 3C5973091E7F74E900BFD00F /* ClipCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5973021E7F74E900BFD00F /* ClipCache.cpp */; }; @@ -1150,6 +1190,80 @@ 3C53C14121C9DCDB00343D85 /* chrono.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = chrono.h; path = ../../../Source/3rdParty/fmt/chrono.h; sourceTree = ""; }; 3C53C14221C9DCDB00343D85 /* color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = color.h; path = ../../../Source/3rdParty/fmt/color.h; sourceTree = ""; }; 3C53C14321C9DCDB00343D85 /* locale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = locale.h; path = ../../../Source/3rdParty/fmt/locale.h; sourceTree = ""; }; + 3C53E9CA2BEB442200039640 /* YGPixelGrid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YGPixelGrid.cpp; path = ../../../Source/3rdParty/yoga/YGPixelGrid.cpp; sourceTree = ""; }; + 3C53E9CB2BEB442200039640 /* Yoga.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Yoga.h; path = ../../../Source/3rdParty/yoga/Yoga.h; sourceTree = ""; }; + 3C53E9CC2BEB442200039640 /* YGMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YGMacros.h; path = ../../../Source/3rdParty/yoga/YGMacros.h; sourceTree = ""; }; + 3C53E9CD2BEB442200039640 /* YGConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YGConfig.cpp; path = ../../../Source/3rdParty/yoga/YGConfig.cpp; sourceTree = ""; }; + 3C53E9CE2BEB442200039640 /* YGPixelGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YGPixelGrid.h; path = ../../../Source/3rdParty/yoga/YGPixelGrid.h; sourceTree = ""; }; + 3C53E9CF2BEB442200039640 /* YGConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YGConfig.h; path = ../../../Source/3rdParty/yoga/YGConfig.h; sourceTree = ""; }; + 3C53E9D02BEB442200039640 /* YGValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YGValue.h; path = ../../../Source/3rdParty/yoga/YGValue.h; sourceTree = ""; }; + 3C53E9D12BEB442200039640 /* YGNodeStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YGNodeStyle.h; path = ../../../Source/3rdParty/yoga/YGNodeStyle.h; sourceTree = ""; }; + 3C53E9D22BEB442200039640 /* YGNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YGNodeLayout.h; path = ../../../Source/3rdParty/yoga/YGNodeLayout.h; sourceTree = ""; }; + 3C53E9D32BEB442300039640 /* YGEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YGEnums.h; path = ../../../Source/3rdParty/yoga/YGEnums.h; sourceTree = ""; }; + 3C53E9D42BEB442300039640 /* YGValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YGValue.cpp; path = ../../../Source/3rdParty/yoga/YGValue.cpp; sourceTree = ""; }; + 3C53E9D52BEB442300039640 /* YGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YGNode.h; path = ../../../Source/3rdParty/yoga/YGNode.h; sourceTree = ""; }; + 3C53E9D62BEB442300039640 /* YGNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YGNode.cpp; path = ../../../Source/3rdParty/yoga/YGNode.cpp; sourceTree = ""; }; + 3C53E9D72BEB442300039640 /* YGNodeStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YGNodeStyle.cpp; path = ../../../Source/3rdParty/yoga/YGNodeStyle.cpp; sourceTree = ""; }; + 3C53E9D82BEB442300039640 /* YGNodeLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YGNodeLayout.cpp; path = ../../../Source/3rdParty/yoga/YGNodeLayout.cpp; sourceTree = ""; }; + 3C53E9D92BEB442300039640 /* YGEnums.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YGEnums.cpp; path = ../../../Source/3rdParty/yoga/YGEnums.cpp; sourceTree = ""; }; + 3C53E9F02BEB448D00039640 /* Style.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Style.h; path = ../../../Source/3rdParty/yoga/style/Style.h; sourceTree = ""; }; + 3C53E9F12BEB448D00039640 /* SmallValueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SmallValueBuffer.h; path = ../../../Source/3rdParty/yoga/style/SmallValueBuffer.h; sourceTree = ""; }; + 3C53E9F22BEB448D00039640 /* StyleLength.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StyleLength.h; path = ../../../Source/3rdParty/yoga/style/StyleLength.h; sourceTree = ""; }; + 3C53E9F32BEB448E00039640 /* StyleValueHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StyleValueHandle.h; path = ../../../Source/3rdParty/yoga/style/StyleValueHandle.h; sourceTree = ""; }; + 3C53E9F42BEB448E00039640 /* StyleValuePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StyleValuePool.h; path = ../../../Source/3rdParty/yoga/style/StyleValuePool.h; sourceTree = ""; }; + 3C53E9F52BEB449F00039640 /* Comparison.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Comparison.h; path = ../../../Source/3rdParty/yoga/numeric/Comparison.h; sourceTree = ""; }; + 3C53E9F62BEB449F00039640 /* FloatOptional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FloatOptional.h; path = ../../../Source/3rdParty/yoga/numeric/FloatOptional.h; sourceTree = ""; }; + 3C53E9F72BEB44B000039640 /* Node.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Node.cpp; path = ../../../Source/3rdParty/yoga/node/Node.cpp; sourceTree = ""; }; + 3C53E9F82BEB44B000039640 /* LayoutResults.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LayoutResults.cpp; path = ../../../Source/3rdParty/yoga/node/LayoutResults.cpp; sourceTree = ""; }; + 3C53E9F92BEB44B000039640 /* Node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Node.h; path = ../../../Source/3rdParty/yoga/node/Node.h; sourceTree = ""; }; + 3C53E9FA2BEB44B000039640 /* CachedMeasurement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CachedMeasurement.h; path = ../../../Source/3rdParty/yoga/node/CachedMeasurement.h; sourceTree = ""; }; + 3C53E9FB2BEB44B000039640 /* LayoutResults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LayoutResults.h; path = ../../../Source/3rdParty/yoga/node/LayoutResults.h; sourceTree = ""; }; + 3C53EA002BEB44C100039640 /* event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = event.cpp; path = ../../../Source/3rdParty/yoga/event/event.cpp; sourceTree = ""; }; + 3C53EA012BEB44C100039640 /* event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = event.h; path = ../../../Source/3rdParty/yoga/event/event.h; sourceTree = ""; }; + 3C53EA042BEB44D100039640 /* Direction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Direction.h; path = ../../../Source/3rdParty/yoga/enums/Direction.h; sourceTree = ""; }; + 3C53EA052BEB44D100039640 /* YogaEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YogaEnums.h; path = ../../../Source/3rdParty/yoga/enums/YogaEnums.h; sourceTree = ""; }; + 3C53EA062BEB44D100039640 /* PhysicalEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicalEdge.h; path = ../../../Source/3rdParty/yoga/enums/PhysicalEdge.h; sourceTree = ""; }; + 3C53EA072BEB44D100039640 /* Display.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Display.h; path = ../../../Source/3rdParty/yoga/enums/Display.h; sourceTree = ""; }; + 3C53EA082BEB44D100039640 /* Edge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Edge.h; path = ../../../Source/3rdParty/yoga/enums/Edge.h; sourceTree = ""; }; + 3C53EA092BEB44D200039640 /* ExperimentalFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExperimentalFeature.h; path = ../../../Source/3rdParty/yoga/enums/ExperimentalFeature.h; sourceTree = ""; }; + 3C53EA0A2BEB44D200039640 /* MeasureMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MeasureMode.h; path = ../../../Source/3rdParty/yoga/enums/MeasureMode.h; sourceTree = ""; }; + 3C53EA0B2BEB44D200039640 /* LogLevel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LogLevel.h; path = ../../../Source/3rdParty/yoga/enums/LogLevel.h; sourceTree = ""; }; + 3C53EA0C2BEB44D200039640 /* Dimension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dimension.h; path = ../../../Source/3rdParty/yoga/enums/Dimension.h; sourceTree = ""; }; + 3C53EA0D2BEB44D200039640 /* Errata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Errata.h; path = ../../../Source/3rdParty/yoga/enums/Errata.h; sourceTree = ""; }; + 3C53EA0E2BEB44D200039640 /* Overflow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Overflow.h; path = ../../../Source/3rdParty/yoga/enums/Overflow.h; sourceTree = ""; }; + 3C53EA0F2BEB44D200039640 /* NodeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NodeType.h; path = ../../../Source/3rdParty/yoga/enums/NodeType.h; sourceTree = ""; }; + 3C53EA102BEB44D200039640 /* PositionType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PositionType.h; path = ../../../Source/3rdParty/yoga/enums/PositionType.h; sourceTree = ""; }; + 3C53EA112BEB44D200039640 /* Wrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Wrap.h; path = ../../../Source/3rdParty/yoga/enums/Wrap.h; sourceTree = ""; }; + 3C53EA122BEB44D200039640 /* Align.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Align.h; path = ../../../Source/3rdParty/yoga/enums/Align.h; sourceTree = ""; }; + 3C53EA132BEB44D200039640 /* Gutter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gutter.h; path = ../../../Source/3rdParty/yoga/enums/Gutter.h; sourceTree = ""; }; + 3C53EA142BEB44D200039640 /* Justify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Justify.h; path = ../../../Source/3rdParty/yoga/enums/Justify.h; sourceTree = ""; }; + 3C53EA152BEB44D200039640 /* FlexDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlexDirection.h; path = ../../../Source/3rdParty/yoga/enums/FlexDirection.h; sourceTree = ""; }; + 3C53EA162BEB44D200039640 /* Unit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Unit.h; path = ../../../Source/3rdParty/yoga/enums/Unit.h; sourceTree = ""; }; + 3C53EA172BEB44E800039640 /* Log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Log.h; path = ../../../Source/3rdParty/yoga/debug/Log.h; sourceTree = ""; }; + 3C53EA182BEB44E800039640 /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Log.cpp; path = ../../../Source/3rdParty/yoga/debug/Log.cpp; sourceTree = ""; }; + 3C53EA192BEB44E800039640 /* AssertFatal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AssertFatal.h; path = ../../../Source/3rdParty/yoga/debug/AssertFatal.h; sourceTree = ""; }; + 3C53EA1A2BEB44E800039640 /* AssertFatal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AssertFatal.cpp; path = ../../../Source/3rdParty/yoga/debug/AssertFatal.cpp; sourceTree = ""; }; + 3C53EA1F2BEB450000039640 /* Config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Config.h; path = ../../../Source/3rdParty/yoga/config/Config.h; sourceTree = ""; }; + 3C53EA202BEB450000039640 /* Config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Config.cpp; path = ../../../Source/3rdParty/yoga/config/Config.cpp; sourceTree = ""; }; + 3C53EA232BEB451900039640 /* Baseline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Baseline.h; path = ../../../Source/3rdParty/yoga/algorithm/Baseline.h; sourceTree = ""; }; + 3C53EA242BEB451A00039640 /* Baseline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Baseline.cpp; path = ../../../Source/3rdParty/yoga/algorithm/Baseline.cpp; sourceTree = ""; }; + 3C53EA252BEB451A00039640 /* CalculateLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CalculateLayout.cpp; path = ../../../Source/3rdParty/yoga/algorithm/CalculateLayout.cpp; sourceTree = ""; }; + 3C53EA262BEB451A00039640 /* PixelGrid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PixelGrid.cpp; path = ../../../Source/3rdParty/yoga/algorithm/PixelGrid.cpp; sourceTree = ""; }; + 3C53EA272BEB451A00039640 /* SizingMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SizingMode.h; path = ../../../Source/3rdParty/yoga/algorithm/SizingMode.h; sourceTree = ""; }; + 3C53EA282BEB451A00039640 /* FlexLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FlexLine.cpp; path = ../../../Source/3rdParty/yoga/algorithm/FlexLine.cpp; sourceTree = ""; }; + 3C53EA292BEB451A00039640 /* Cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Cache.cpp; path = ../../../Source/3rdParty/yoga/algorithm/Cache.cpp; sourceTree = ""; }; + 3C53EA2A2BEB451A00039640 /* AbsoluteLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AbsoluteLayout.h; path = ../../../Source/3rdParty/yoga/algorithm/AbsoluteLayout.h; sourceTree = ""; }; + 3C53EA2B2BEB451A00039640 /* Cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Cache.h; path = ../../../Source/3rdParty/yoga/algorithm/Cache.h; sourceTree = ""; }; + 3C53EA2C2BEB451A00039640 /* Align.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Align.h; path = ../../../Source/3rdParty/yoga/algorithm/Align.h; sourceTree = ""; }; + 3C53EA2D2BEB451A00039640 /* CalculateLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CalculateLayout.h; path = ../../../Source/3rdParty/yoga/algorithm/CalculateLayout.h; sourceTree = ""; }; + 3C53EA2E2BEB451A00039640 /* FlexDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlexDirection.h; path = ../../../Source/3rdParty/yoga/algorithm/FlexDirection.h; sourceTree = ""; }; + 3C53EA2F2BEB451A00039640 /* TrailingPosition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TrailingPosition.h; path = ../../../Source/3rdParty/yoga/algorithm/TrailingPosition.h; sourceTree = ""; }; + 3C53EA302BEB451A00039640 /* FlexLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlexLine.h; path = ../../../Source/3rdParty/yoga/algorithm/FlexLine.h; sourceTree = ""; }; + 3C53EA312BEB451A00039640 /* AbsoluteLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AbsoluteLayout.cpp; path = ../../../Source/3rdParty/yoga/algorithm/AbsoluteLayout.cpp; sourceTree = ""; }; + 3C53EA322BEB451A00039640 /* PixelGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PixelGrid.h; path = ../../../Source/3rdParty/yoga/algorithm/PixelGrid.h; sourceTree = ""; }; + 3C53EA332BEB451A00039640 /* BoundAxis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BoundAxis.h; path = ../../../Source/3rdParty/yoga/algorithm/BoundAxis.h; sourceTree = ""; }; + 3C53EA402BEB456700039640 /* AlignNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AlignNode.h; path = ../../../Source/Node/AlignNode.h; sourceTree = ""; }; + 3C53EA412BEB456700039640 /* AlignNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AlignNode.cpp; path = ../../../Source/Node/AlignNode.cpp; sourceTree = ""; }; 3C5972F81E7F74CB00BFD00F /* Animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Animation.cpp; path = ../../../Source/Animation/Animation.cpp; sourceTree = ""; }; 3C5972F91E7F74CB00BFD00F /* Animation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Animation.h; path = ../../../Source/Animation/Animation.h; sourceTree = ""; }; 3C5972FC1E7F74CB00BFD00F /* ModelDef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ModelDef.cpp; path = ../../../Source/Animation/ModelDef.cpp; sourceTree = ""; }; @@ -2055,6 +2169,8 @@ 3C1038511E15103600D37A60 /* Node */ = { isa = PBXGroup; children = ( + 3C53EA412BEB456700039640 /* AlignNode.cpp */, + 3C53EA402BEB456700039640 /* AlignNode.h */, 3C28C0A12772CFE4007FC76E /* Grid.cpp */, 3C28C0A02772CFE4007FC76E /* Grid.h */, 3C030CB924CAC2B80074786E /* ClipNode.cpp */, @@ -2578,6 +2694,149 @@ name = Http; sourceTree = ""; }; + 3C53E9C92BEB440A00039640 /* yoga */ = { + isa = PBXGroup; + children = ( + 3C53E9EF2BEB447200039640 /* style */, + 3C53E9EE2BEB446500039640 /* numeric */, + 3C53E9ED2BEB445E00039640 /* node */, + 3C53E9EC2BEB445700039640 /* event */, + 3C53E9EB2BEB444F00039640 /* enums */, + 3C53E9EA2BEB444900039640 /* debug */, + 3C53E9E92BEB443D00039640 /* config */, + 3C53E9E82BEB442800039640 /* algorithm */, + 3C53E9CD2BEB442200039640 /* YGConfig.cpp */, + 3C53E9CF2BEB442200039640 /* YGConfig.h */, + 3C53E9D92BEB442300039640 /* YGEnums.cpp */, + 3C53E9D32BEB442300039640 /* YGEnums.h */, + 3C53E9CC2BEB442200039640 /* YGMacros.h */, + 3C53E9D62BEB442300039640 /* YGNode.cpp */, + 3C53E9D52BEB442300039640 /* YGNode.h */, + 3C53E9D82BEB442300039640 /* YGNodeLayout.cpp */, + 3C53E9D22BEB442200039640 /* YGNodeLayout.h */, + 3C53E9D72BEB442300039640 /* YGNodeStyle.cpp */, + 3C53E9D12BEB442200039640 /* YGNodeStyle.h */, + 3C53E9CA2BEB442200039640 /* YGPixelGrid.cpp */, + 3C53E9CE2BEB442200039640 /* YGPixelGrid.h */, + 3C53E9D42BEB442300039640 /* YGValue.cpp */, + 3C53E9D02BEB442200039640 /* YGValue.h */, + 3C53E9CB2BEB442200039640 /* Yoga.h */, + ); + name = yoga; + sourceTree = ""; + }; + 3C53E9E82BEB442800039640 /* algorithm */ = { + isa = PBXGroup; + children = ( + 3C53EA312BEB451A00039640 /* AbsoluteLayout.cpp */, + 3C53EA2A2BEB451A00039640 /* AbsoluteLayout.h */, + 3C53EA2C2BEB451A00039640 /* Align.h */, + 3C53EA242BEB451A00039640 /* Baseline.cpp */, + 3C53EA232BEB451900039640 /* Baseline.h */, + 3C53EA332BEB451A00039640 /* BoundAxis.h */, + 3C53EA292BEB451A00039640 /* Cache.cpp */, + 3C53EA2B2BEB451A00039640 /* Cache.h */, + 3C53EA252BEB451A00039640 /* CalculateLayout.cpp */, + 3C53EA2D2BEB451A00039640 /* CalculateLayout.h */, + 3C53EA2E2BEB451A00039640 /* FlexDirection.h */, + 3C53EA282BEB451A00039640 /* FlexLine.cpp */, + 3C53EA302BEB451A00039640 /* FlexLine.h */, + 3C53EA262BEB451A00039640 /* PixelGrid.cpp */, + 3C53EA322BEB451A00039640 /* PixelGrid.h */, + 3C53EA272BEB451A00039640 /* SizingMode.h */, + 3C53EA2F2BEB451A00039640 /* TrailingPosition.h */, + ); + name = algorithm; + sourceTree = ""; + }; + 3C53E9E92BEB443D00039640 /* config */ = { + isa = PBXGroup; + children = ( + 3C53EA202BEB450000039640 /* Config.cpp */, + 3C53EA1F2BEB450000039640 /* Config.h */, + ); + name = config; + sourceTree = ""; + }; + 3C53E9EA2BEB444900039640 /* debug */ = { + isa = PBXGroup; + children = ( + 3C53EA1A2BEB44E800039640 /* AssertFatal.cpp */, + 3C53EA192BEB44E800039640 /* AssertFatal.h */, + 3C53EA182BEB44E800039640 /* Log.cpp */, + 3C53EA172BEB44E800039640 /* Log.h */, + ); + name = debug; + sourceTree = ""; + }; + 3C53E9EB2BEB444F00039640 /* enums */ = { + isa = PBXGroup; + children = ( + 3C53EA122BEB44D200039640 /* Align.h */, + 3C53EA0C2BEB44D200039640 /* Dimension.h */, + 3C53EA042BEB44D100039640 /* Direction.h */, + 3C53EA072BEB44D100039640 /* Display.h */, + 3C53EA082BEB44D100039640 /* Edge.h */, + 3C53EA0D2BEB44D200039640 /* Errata.h */, + 3C53EA092BEB44D200039640 /* ExperimentalFeature.h */, + 3C53EA152BEB44D200039640 /* FlexDirection.h */, + 3C53EA132BEB44D200039640 /* Gutter.h */, + 3C53EA142BEB44D200039640 /* Justify.h */, + 3C53EA0B2BEB44D200039640 /* LogLevel.h */, + 3C53EA0A2BEB44D200039640 /* MeasureMode.h */, + 3C53EA0F2BEB44D200039640 /* NodeType.h */, + 3C53EA0E2BEB44D200039640 /* Overflow.h */, + 3C53EA062BEB44D100039640 /* PhysicalEdge.h */, + 3C53EA102BEB44D200039640 /* PositionType.h */, + 3C53EA162BEB44D200039640 /* Unit.h */, + 3C53EA112BEB44D200039640 /* Wrap.h */, + 3C53EA052BEB44D100039640 /* YogaEnums.h */, + ); + name = enums; + sourceTree = ""; + }; + 3C53E9EC2BEB445700039640 /* event */ = { + isa = PBXGroup; + children = ( + 3C53EA002BEB44C100039640 /* event.cpp */, + 3C53EA012BEB44C100039640 /* event.h */, + ); + name = event; + sourceTree = ""; + }; + 3C53E9ED2BEB445E00039640 /* node */ = { + isa = PBXGroup; + children = ( + 3C53E9FA2BEB44B000039640 /* CachedMeasurement.h */, + 3C53E9F82BEB44B000039640 /* LayoutResults.cpp */, + 3C53E9FB2BEB44B000039640 /* LayoutResults.h */, + 3C53E9F72BEB44B000039640 /* Node.cpp */, + 3C53E9F92BEB44B000039640 /* Node.h */, + ); + name = node; + sourceTree = ""; + }; + 3C53E9EE2BEB446500039640 /* numeric */ = { + isa = PBXGroup; + children = ( + 3C53E9F52BEB449F00039640 /* Comparison.h */, + 3C53E9F62BEB449F00039640 /* FloatOptional.h */, + ); + name = numeric; + sourceTree = ""; + }; + 3C53E9EF2BEB447200039640 /* style */ = { + isa = PBXGroup; + children = ( + 3C53E9F12BEB448D00039640 /* SmallValueBuffer.h */, + 3C53E9F02BEB448D00039640 /* Style.h */, + 3C53E9F22BEB448D00039640 /* StyleLength.h */, + 3C53E9F32BEB448E00039640 /* StyleValueHandle.h */, + 3C53E9F42BEB448E00039640 /* StyleValuePool.h */, + ); + name = style; + sourceTree = ""; + }; 3C62B46627000F7200294B2D /* soloud */ = { isa = PBXGroup; children = ( @@ -3208,6 +3467,7 @@ 3CB410221E05B08B00A8804D /* 3rdParty */ = { isa = PBXGroup; children = ( + 3C53E9C92BEB440A00039640 /* yoga */, 3C7FA3372B68851F005A8441 /* zlib */, 3C5399F22998DECB00E9125C /* colib */, 3CFEBDAC269308CB006D9974 /* dragonBones */, @@ -3877,9 +4137,11 @@ 3CF10F9F2874C81C00940268 /* TransformConstraintTimeline.cpp in Sources */, 3CFEA16F285B873B002D106F /* m3_api_wasi.c in Sources */, 3C6E8036272A404A00E9F115 /* soloud_fft.cpp in Sources */, + 3C53EA432BEB456700039640 /* AlignNode.cpp in Sources */, 3CF110012874C81C00940268 /* Skeleton.cpp in Sources */, 3C6E8012272A404A00E9F115 /* soloud_speech.cpp in Sources */, 3CF10FC32874C81C00940268 /* Event.cpp in Sources */, + 3C53EA3F2BEB451A00039640 /* AbsoluteLayout.cpp in Sources */, 3C6E804A272A404A00E9F115 /* soloud_misc.cpp in Sources */, 3CF10FE52874C81C00940268 /* SkeletonData.cpp in Sources */, 3CFEA171285B873B002D106F /* m3_parse.c in Sources */, @@ -3946,6 +4208,7 @@ 3C1A3BF7272A8820003A2829 /* lvm.c in Sources */, 3C1A3BF1272A8820003A2829 /* loslib.c in Sources */, 3CFEA157285B873B002D106F /* m3_compile.c in Sources */, + 3C53EA392BEB451A00039640 /* PixelGrid.cpp in Sources */, 3CF110032874C81C00940268 /* TransformConstraintData.cpp in Sources */, 3C6E8048272A404A00E9F115 /* soloud_core_3d.cpp in Sources */, 3C6E801C272A404A00E9F115 /* soloud_noise.cpp in Sources */, @@ -3962,6 +4225,7 @@ 3C1A3BCD272A8820003A2829 /* lctype.c in Sources */, 3C6E803C272A404A00E9F115 /* soloud_filter.cpp in Sources */, 3C6E7FD4272A404900E9F115 /* soloud_echofilter.cpp in Sources */, + 3C53E9DD2BEB442300039640 /* YGConfig.cpp in Sources */, 3C6E8022272A404A00E9F115 /* soloud_openmpt_dll.c in Sources */, 3CA780772AE66FB000B7BADA /* StatsResource.cpp in Sources */, 3C6E8024272A404A00E9F115 /* soloud_openmpt.cpp in Sources */, @@ -3995,11 +4259,13 @@ 3CFEBE1F26930AA1006D9974 /* Armature.cpp in Sources */, 3C6E803A272A404A00E9F115 /* soloud_core_voiceops.cpp in Sources */, 3CFEBE2026930AA1006D9974 /* Bone.cpp in Sources */, + 3C53E9E72BEB442300039640 /* YGEnums.cpp in Sources */, 3CF10FE92874C81C00940268 /* SpineObject.cpp in Sources */, 3CFEBE2126930AA1006D9974 /* Constraint.cpp in Sources */, 3CA780892AE66FB000B7BADA /* StepConf.cpp in Sources */, 3CFEBE2226930AA1006D9974 /* DeformVertices.cpp in Sources */, 3C1A3BF9272A8820003A2829 /* lstring.c in Sources */, + 3C53EA352BEB451A00039640 /* Baseline.cpp in Sources */, 3CFEBE2326930AA1006D9974 /* Slot.cpp in Sources */, 3C6E7FE4272A404900E9F115 /* soloud_waveshaperfilter.cpp in Sources */, 3CFEBE2426930AA1006D9974 /* TransformObject.cpp in Sources */, @@ -4013,6 +4279,7 @@ 3C6E7FD2272A404900E9F115 /* soloud_bassboostfilter.cpp in Sources */, 3CFEBE2726930AA1006D9974 /* EventObject.cpp in Sources */, 3CFEBE2826930AA1006D9974 /* BaseFactory.cpp in Sources */, + 3C53EA222BEB450000039640 /* Config.cpp in Sources */, 3CFEBE2926930AA1006D9974 /* Point.cpp in Sources */, 3CFEBE2A26930AA1006D9974 /* Transform.cpp in Sources */, 3CA780792AE66FB000B7BADA /* MemoryResource.cpp in Sources */, @@ -4042,6 +4309,7 @@ 3CF10FDD2874C81C00940268 /* ConstraintData.cpp in Sources */, 3CFEBE3426930AA1006D9974 /* TextureAtlasData.cpp in Sources */, 3CF10FFB2874C81C00940268 /* Json.cpp in Sources */, + 3C53E9DB2BEB442300039640 /* YGPixelGrid.cpp in Sources */, 3CFEBE3526930AA1006D9974 /* UserData.cpp in Sources */, 3CFEBE3626930AA1006D9974 /* BinaryDataParser.cpp in Sources */, 3CA780872AE66FB000B7BADA /* DynamicMemory.cpp in Sources */, @@ -4107,11 +4375,13 @@ 3CBE0E1326087F7B003F4F45 /* atlas.cpp in Sources */, 3CA7800B2AE66FB000B7BADA /* MotorJointConf.cpp in Sources */, 3CF1100B2874C81C00940268 /* AnimationState.cpp in Sources */, + 3C53E9E32BEB442300039640 /* YGNodeStyle.cpp in Sources */, 3C6E8014272A404A00E9F115 /* ted.cpp in Sources */, 3CBE0E1626087F7B003F4F45 /* Keyboard.cpp in Sources */, 3CBE0E1726087F7B003F4F45 /* stb.cpp in Sources */, 3CF10FA12874C81C00940268 /* PathConstraintSpacingTimeline.cpp in Sources */, 3CF10F9B2874C81C00940268 /* SequenceTimeline.cpp in Sources */, + 3C53EA1E2BEB44E800039640 /* AssertFatal.cpp in Sources */, 3CBE0E1826087F7B003F4F45 /* imgui_tables.cpp in Sources */, 3C6E7FF0272A404900E9F115 /* stb_vorbis.c in Sources */, 3CBE0E1A26087F7B003F4F45 /* VGRender.cpp in Sources */, @@ -4138,6 +4408,7 @@ 3CBE0E2B26087F7B003F4F45 /* ModelCache.cpp in Sources */, 3CA7808F2AE66FB000B7BADA /* Island.cpp in Sources */, 3CBE0E2F26087F7B003F4F45 /* Slice.cpp in Sources */, + 3C53EA032BEB44C100039640 /* event.cpp in Sources */, 3CA780932AE66FB000B7BADA /* MovementConf.cpp in Sources */, 3CA7807F2AE66FB000B7BADA /* Math.cpp in Sources */, 3CA780812AE66FB000B7BADA /* ToiConf.cpp in Sources */, @@ -4176,6 +4447,7 @@ 3CA780672AE66FB000B7BADA /* DistanceJointConf.cpp in Sources */, 3C7FA3542B688547005A8441 /* inflate.c in Sources */, 3C1A3BCF272A8820003A2829 /* lundump.c in Sources */, + 3C53EA372BEB451A00039640 /* CalculateLayout.cpp in Sources */, 3C6E804E272A40B400E9F115 /* sqlite3.c in Sources */, 3CBE0E5126087F7B003F4F45 /* SAXParser.cpp in Sources */, 3CBE0E5326087F7B003F4F45 /* Geometry.cpp in Sources */, @@ -4217,6 +4489,7 @@ 3CBE0E8726087F7B003F4F45 /* Animation.cpp in Sources */, 3CBE0E8B26087F7B003F4F45 /* ImGuiDora.cpp in Sources */, 3CBE0E8C26087F7B003F4F45 /* tolua_map.cpp in Sources */, + 3C53E9DF2BEB442300039640 /* YGValue.cpp in Sources */, 3CBE0E8F26087F7B003F4F45 /* utf8.cpp in Sources */, 3CBE0E9426087F7B003F4F45 /* ClipCache.cpp in Sources */, 3C28C0A32772CFE4007FC76E /* Grid.cpp in Sources */, @@ -4229,12 +4502,14 @@ 3CBE0E9D26087F7B003F4F45 /* LuaBinding.cpp in Sources */, 3CF10FE32874C81C00940268 /* Timeline.cpp in Sources */, 3CBE0EA026087F7B003F4F45 /* ast.cpp in Sources */, + 3C53EA3D2BEB451A00039640 /* Cache.cpp in Sources */, 3CF1100D2874C81C00940268 /* DeformTimeline.cpp in Sources */, 3CBE0EA226087F7B003F4F45 /* nanovg.cpp in Sources */, 3CBE0EA526087F7B003F4F45 /* Camera.cpp in Sources */, 3CA780092AE66FB000B7BADA /* Manifold.cpp in Sources */, 3CBE0EA726087F7B003F4F45 /* Content.mm in Sources */, 3CBE0EA826087F7B003F4F45 /* Value.cpp in Sources */, + 3C53EA3B2BEB451A00039640 /* FlexLine.cpp in Sources */, 3CA31652299234DF007879C4 /* soloud_duckfilter.cpp in Sources */, 3CBE0EA926087F7B003F4F45 /* LuaManual.cpp in Sources */, 3C7FA3662B688547005A8441 /* gzread.c in Sources */, @@ -4248,6 +4523,7 @@ 3CF10FCF2874C81C00940268 /* Attachment.cpp in Sources */, 3CBE0EB026087F7B003F4F45 /* EventType.cpp in Sources */, 3CF10FD72874C81C00940268 /* Bone.cpp in Sources */, + 3C53E9E12BEB442300039640 /* YGNode.cpp in Sources */, 3CBE0EB126087F7B003F4F45 /* LuaCode.cpp in Sources */, 3C6E7FE2272A404900E9F115 /* soloud_biquadresonantfilter.cpp in Sources */, 3CBE0EB326087F7B003F4F45 /* DrawNode.cpp in Sources */, @@ -4271,16 +4547,19 @@ 3CBE0ECD26087F7B003F4F45 /* ShaderCache.cpp in Sources */, 3CA780592AE66FB000B7BADA /* RevoluteJointConf.cpp in Sources */, 3C7FA3562B688547005A8441 /* zutil.c in Sources */, + 3C53E9FD2BEB44B000039640 /* Node.cpp in Sources */, 3CA7805D2AE66FB000B7BADA /* PrismaticJointConf.cpp in Sources */, 3CF10FDF2874C81C00940268 /* SkeletonJson.cpp in Sources */, 3CBE0ECE26087F7B003F4F45 /* lodepng.cpp in Sources */, 3CBE0ED826087F7B003F4F45 /* PhysicsWorld.cpp in Sources */, 3CBE0EDA26087F7B003F4F45 /* AutoreleasePool.cpp in Sources */, 3C7FA3642B688547005A8441 /* inffast.c in Sources */, + 3C53E9FF2BEB44B000039640 /* LayoutResults.cpp in Sources */, 3CBE0EDE26087F7B003F4F45 /* AINode.cpp in Sources */, 3CA780432AE66FB000B7BADA /* WorldShape.cpp in Sources */, 3CFEA161285B873B002D106F /* m3_core.c in Sources */, 3CBE0EDF26087F7B003F4F45 /* tolua_fix.cpp in Sources */, + 3C53EA1C2BEB44E800039640 /* Log.cpp in Sources */, 3CBE0EE126087F7B003F4F45 /* Array.cpp in Sources */, 3CBE0EE726087F7B003F4F45 /* Particle.cpp in Sources */, 3CBE0EE826087F7B003F4F45 /* Dictionary.cpp in Sources */, @@ -4288,6 +4567,7 @@ 3CA780832AE66FB000B7BADA /* Version.cpp in Sources */, 3C6E7FE0272A404900E9F115 /* soloud_freeverbfilter.cpp in Sources */, 3CA7800D2AE66FB000B7BADA /* Simplex.cpp in Sources */, + 3C53E9E52BEB442300039640 /* YGNodeLayout.cpp in Sources */, 3C6E7FDC272A404900E9F115 /* soloud_lofifilter.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4307,9 +4587,11 @@ 3CF10F9E2874C81C00940268 /* TransformConstraintTimeline.cpp in Sources */, 3C6E8049272A404A00E9F115 /* soloud_misc.cpp in Sources */, 3C131FFC21011BB60087154A /* BulletDef.cpp in Sources */, + 3C53EA422BEB456700039640 /* AlignNode.cpp in Sources */, 3CF110002874C81C00940268 /* Skeleton.cpp in Sources */, 3CFEA170285B873B002D106F /* m3_parse.c in Sources */, 3CF10FC22874C81C00940268 /* Event.cpp in Sources */, + 3C53EA3E2BEB451A00039640 /* AbsoluteLayout.cpp in Sources */, 3C1A3C0C272A8820003A2829 /* lauxlib.c in Sources */, 3CF10FE42874C81C00940268 /* SkeletonData.cpp in Sources */, 3CFEA164285B873B002D106F /* m3_info.c in Sources */, @@ -4376,6 +4658,7 @@ 3C6577481ECC2C9D00C5FB09 /* Keyboard.cpp in Sources */, 3CFEBE1726930A3A006D9974 /* DataParser.cpp in Sources */, 3CE9509D20F33DA9002FFC18 /* stb.cpp in Sources */, + 3C53EA382BEB451A00039640 /* PixelGrid.cpp in Sources */, 3C6E802B272A404A00E9F115 /* soloud_bus.cpp in Sources */, 3CFEBDEE26930A16006D9974 /* Point.cpp in Sources */, 3CB2116325C63C3A00CEA033 /* imgui_tables.cpp in Sources */, @@ -4392,6 +4675,7 @@ 3C1A3C08272A8820003A2829 /* lutf8lib.c in Sources */, 3C030CCE24CAC2BA0074786E /* Model.cpp in Sources */, 3C4BC6641E17F1B500292200 /* tolua_event.cpp in Sources */, + 3C53E9DC2BEB442300039640 /* YGConfig.cpp in Sources */, 3C883B1C21E338F500BFD758 /* Director.cpp in Sources */, 3CA780762AE66FB000B7BADA /* StatsResource.cpp in Sources */, 3C5399F62998DEF800E9125C /* ljson.c in Sources */, @@ -4425,11 +4709,13 @@ 3CF10FD02874C81C00940268 /* Slot.cpp in Sources */, 3CAA2D1726155DB3004443A6 /* Transaction.cpp in Sources */, 3CF10FA62874C81C00940268 /* RTTI.cpp in Sources */, + 3C53E9E62BEB442300039640 /* YGEnums.cpp in Sources */, 3CF00E39258B475A00898F4B /* SVGCache.cpp in Sources */, 3C131FFB21011BB60087154A /* Bullet.cpp in Sources */, 3CA780882AE66FB000B7BADA /* StepConf.cpp in Sources */, 3CBFB70C264376D4006348C8 /* implot_items.cpp in Sources */, 3CF10FF22874C81C00940268 /* IkConstraintData.cpp in Sources */, + 3C53EA342BEB451A00039640 /* Baseline.cpp in Sources */, 3C1A3BDC272A8820003A2829 /* lbaselib.c in Sources */, 3C41D60621490BE6003FDDFE /* imgui_demo.cpp in Sources */, 3C92DEC41FFE1CFA003AF655 /* Entity.cpp in Sources */, @@ -4443,6 +4729,7 @@ 3C41D60521490BE6003FDDFE /* imgui_widgets.cpp in Sources */, 3CF110022874C81C00940268 /* TransformConstraintData.cpp in Sources */, 3C1A3BF6272A8820003A2829 /* lvm.c in Sources */, + 3C53EA212BEB450000039640 /* Config.cpp in Sources */, 3CA1B7F51EC16ED300BC58FF /* SAXParser.cpp in Sources */, 3C1A3BF0272A8820003A2829 /* loslib.c in Sources */, 3CA780782AE66FB000B7BADA /* MemoryResource.cpp in Sources */, @@ -4472,6 +4759,7 @@ 3CF110082874C81C00940268 /* PathConstraintPositionTimeline.cpp in Sources */, 3C6E8045272A404A00E9F115 /* soloud_core_getters.cpp in Sources */, 3C6E7FDD272A404900E9F115 /* soloud_flangerfilter.cpp in Sources */, + 3C53E9DA2BEB442300039640 /* YGPixelGrid.cpp in Sources */, 3CE355621E1B7A6300268A19 /* LuaHandler.cpp in Sources */, 3C6E7FF3272A404900E9F115 /* dr_impl.cpp in Sources */, 3CA780862AE66FB000B7BADA /* DynamicMemory.cpp in Sources */, @@ -4537,11 +4825,13 @@ 3CA780722AE66FB000B7BADA /* BlockAllocator.cpp in Sources */, 3C6E7FE3272A404900E9F115 /* soloud_waveshaperfilter.cpp in Sources */, 3CA7800A2AE66FB000B7BADA /* MotorJointConf.cpp in Sources */, + 3C53E9E22BEB442300039640 /* YGNodeStyle.cpp in Sources */, 3CF10FCC2874C81C00940268 /* CurveTimeline.cpp in Sources */, 3CF10FB42874C81C00940268 /* Sequence.cpp in Sources */, 3CFEBE0C26930A26006D9974 /* TextureAtlasData.cpp in Sources */, 3C1A3BD0272A8820003A2829 /* ldebug.c in Sources */, 3C6E7FD1272A404900E9F115 /* soloud_bassboostfilter.cpp in Sources */, + 3C53EA1D2BEB44E800039640 /* AssertFatal.cpp in Sources */, 3CF10FAA2874C81C00940268 /* BoundingBoxAttachment.cpp in Sources */, 3C883B1821E338F500BFD758 /* Application.cpp in Sources */, 3CF10FFC2874C81C00940268 /* SkeletonBinary.cpp in Sources */, @@ -4568,6 +4858,7 @@ 3CF110102874C81C00940268 /* TextureLoader.cpp in Sources */, 3CA7808E2AE66FB000B7BADA /* Island.cpp in Sources */, 3CE534761E5A9BD500F017AE /* Singleton.cpp in Sources */, + 3C53EA022BEB44C100039640 /* event.cpp in Sources */, 3CA780922AE66FB000B7BADA /* MovementConf.cpp in Sources */, 3CA7807E2AE66FB000B7BADA /* Math.cpp in Sources */, 3CA780802AE66FB000B7BADA /* ToiConf.cpp in Sources */, @@ -4606,6 +4897,7 @@ 3CA780662AE66FB000B7BADA /* DistanceJointConf.cpp in Sources */, 3C7FA3532B688547005A8441 /* inflate.c in Sources */, 3C9CCD0A2689D7670086757D /* os.cpp in Sources */, + 3C53EA362BEB451A00039640 /* CalculateLayout.cpp in Sources */, 3C883B1A21E338F500BFD758 /* Camera.cpp in Sources */, 3C883B1521E338F500BFD758 /* Content.mm in Sources */, 3C6E7FEF272A404900E9F115 /* stb_vorbis.c in Sources */, @@ -4647,6 +4939,7 @@ 3C883B1B21E338F500BFD758 /* Renderer.cpp in Sources */, 3C1A3BCE272A8820003A2829 /* lundump.c in Sources */, 3C853F8A28E3ED8A0012C0D0 /* Test.cpp in Sources */, + 3C53E9DE2BEB442300039640 /* YGValue.cpp in Sources */, 3C6E804D272A40B400E9F115 /* sqlite3.c in Sources */, 3CAA2D1A26155DB3004443A6 /* Statement.cpp in Sources */, 3C030CC824CAC2B90074786E /* Label.cpp in Sources */, @@ -4659,12 +4952,14 @@ 3C1A3BF4272A8820003A2829 /* lapi.c in Sources */, 3C01B6C21E96433600A0CC1C /* SoundCache.cpp in Sources */, 3C41D61021490CFB003FDDFE /* nanovg_bgfx.cpp in Sources */, + 3C53EA3C2BEB451A00039640 /* Cache.cpp in Sources */, 3C030CD024CAC2BA0074786E /* VGNode.cpp in Sources */, 3C6E7FF7272A404900E9F115 /* sndchip.cpp in Sources */, 3CFEA16C285B873B002D106F /* m3_bind.c in Sources */, 3CA780082AE66FB000B7BADA /* Manifold.cpp in Sources */, 3CFEBDC4269309C7006D9974 /* AnimationState.cpp in Sources */, 3C25B872257A7F8B002DD8E1 /* yuescript.cpp in Sources */, + 3C53EA3A2BEB451A00039640 /* FlexLine.cpp in Sources */, 3CD6C2A11E30C14800B36FFC /* ShaderCache.cpp in Sources */, 3CFEBDD6269309D5006D9974 /* DeformVertices.cpp in Sources */, 3C7FA3652B688547005A8441 /* gzread.c in Sources */, @@ -4678,6 +4973,7 @@ 3CBFB70A264376D4006348C8 /* implot_demo.cpp in Sources */, 3C1A3BEC272A8820003A2829 /* lstrlib.c in Sources */, 3CFEBE1826930A3A006D9974 /* JSONDataParser.cpp in Sources */, + 3C53E9E02BEB442300039640 /* YGNode.cpp in Sources */, 3CA31651299234DE007879C4 /* soloud_duckfilter.cpp in Sources */, 3CFEBDD8269309D5006D9974 /* Armature.cpp in Sources */, 3CFEBDDE269309E6006D9974 /* BaseObject.cpp in Sources */, @@ -4701,16 +4997,19 @@ 3CFEBE0D26930A26006D9974 /* SkinData.cpp in Sources */, 3CA780582AE66FB000B7BADA /* RevoluteJointConf.cpp in Sources */, 3C7FA3552B688547005A8441 /* zutil.c in Sources */, + 3C53E9FC2BEB44B000039640 /* Node.cpp in Sources */, 3CA7805C2AE66FB000B7BADA /* PrismaticJointConf.cpp in Sources */, 3CFEBE0F26930A26006D9974 /* AnimationData.cpp in Sources */, 3C1A3BFE272A8820003A2829 /* lcorolib.c in Sources */, 3CF10FDE2874C81C00940268 /* SkeletonJson.cpp in Sources */, 3C030CCA24CAC2B90074786E /* Particle.cpp in Sources */, 3C7FA3632B688547005A8441 /* inffast.c in Sources */, + 3C53E9FE2BEB44B000039640 /* LayoutResults.cpp in Sources */, 3CB730821E80344C006DFA18 /* Dictionary.cpp in Sources */, 3CA780422AE66FB000B7BADA /* WorldShape.cpp in Sources */, 3CFEBE0A26930A26006D9974 /* ArmatureData.cpp in Sources */, 3CFEA160285B873B002D106F /* m3_core.c in Sources */, + 3C53EA1B2BEB44E800039640 /* Log.cpp in Sources */, 3CFEBDC1269309C7006D9974 /* TimelineState.cpp in Sources */, 3CFEBDD7269309D5006D9974 /* TransformObject.cpp in Sources */, 3CFEBE0626930A26006D9974 /* ConstraintData.cpp in Sources */, @@ -4718,6 +5017,7 @@ 3CA780822AE66FB000B7BADA /* Version.cpp in Sources */, 3C6E7FDF272A404900E9F115 /* soloud_freeverbfilter.cpp in Sources */, 3CA7800C2AE66FB000B7BADA /* Simplex.cpp in Sources */, + 3C53E9E42BEB442300039640 /* YGNodeLayout.cpp in Sources */, 3C6E7FDB272A404900E9F115 /* soloud_lofifilter.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Project/iOS/Dora/Info.plist b/Project/iOS/Dora/Info.plist index 7da4b10e0..8f181407b 100644 --- a/Project/iOS/Dora/Info.plist +++ b/Project/iOS/Dora/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.22 + 1.3.23 CFBundleSignature ???? CFBundleVersion - 54 + 55 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/Project/macOS/Dora.xcodeproj/project.pbxproj b/Project/macOS/Dora.xcodeproj/project.pbxproj index f36d66ec1..538b0013e 100644 --- a/Project/macOS/Dora.xcodeproj/project.pbxproj +++ b/Project/macOS/Dora.xcodeproj/project.pbxproj @@ -111,6 +111,26 @@ 3C49FFEF20EF644900E775D7 /* stb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C49FFEE20EF644900E775D7 /* stb.cpp */; }; 3C5399E82994E97400E9125C /* HttpServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C5399E62994E97400E9125C /* HttpServer.cpp */; }; 3C5399EB2998BAF000E9125C /* ljson.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C5399EA2998BAF000E9125C /* ljson.c */; }; + 3C53E9372BE8CF1100039640 /* AbsoluteLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E8E62BE8CF1100039640 /* AbsoluteLayout.cpp */; }; + 3C53E9382BE8CF1100039640 /* Baseline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E8E92BE8CF1100039640 /* Baseline.cpp */; }; + 3C53E9392BE8CF1100039640 /* Cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E8EC2BE8CF1100039640 /* Cache.cpp */; }; + 3C53E93A2BE8CF1100039640 /* CalculateLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E8EE2BE8CF1100039640 /* CalculateLayout.cpp */; }; + 3C53E93B2BE8CF1100039640 /* FlexLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E8F12BE8CF1100039640 /* FlexLine.cpp */; }; + 3C53E93C2BE8CF1100039640 /* PixelGrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E8F32BE8CF1100039640 /* PixelGrid.cpp */; }; + 3C53E93D2BE8CF1100039640 /* Config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E8F82BE8CF1100039640 /* Config.cpp */; }; + 3C53E93E2BE8CF1100039640 /* AssertFatal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E8FB2BE8CF1100039640 /* AssertFatal.cpp */; }; + 3C53E93F2BE8CF1100039640 /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E8FD2BE8CF1100039640 /* Log.cpp */; }; + 3C53E9402BE8CF1100039640 /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9142BE8CF1100039640 /* event.cpp */; }; + 3C53E9412BE8CF1100039640 /* LayoutResults.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9182BE8CF1100039640 /* LayoutResults.cpp */; }; + 3C53E9422BE8CF1100039640 /* Node.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E91A2BE8CF1100039640 /* Node.cpp */; }; + 3C53E9432BE8CF1100039640 /* YGConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9262BE8CF1100039640 /* YGConfig.cpp */; }; + 3C53E9442BE8CF1100039640 /* YGEnums.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9282BE8CF1100039640 /* YGEnums.cpp */; }; + 3C53E9452BE8CF1100039640 /* YGNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E92B2BE8CF1100039640 /* YGNode.cpp */; }; + 3C53E9462BE8CF1100039640 /* YGNodeLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E92D2BE8CF1100039640 /* YGNodeLayout.cpp */; }; + 3C53E9472BE8CF1100039640 /* YGNodeStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E92F2BE8CF1100039640 /* YGNodeStyle.cpp */; }; + 3C53E9482BE8CF1100039640 /* YGPixelGrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9312BE8CF1100039640 /* YGPixelGrid.cpp */; }; + 3C53E9492BE8CF1100039640 /* YGValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E9332BE8CF1100039640 /* YGValue.cpp */; }; + 3C53E94C2BE9C77D00039640 /* AlignNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C53E94A2BE9C77D00039640 /* AlignNode.cpp */; }; 3C54F0DA2599CD65003D8F08 /* Value.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C54F0D92599CD65003D8F08 /* Value.cpp */; }; 3C57A3A11E0A05AD002141DF /* Event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C57A39B1E0A05AD002141DF /* Event.cpp */; }; 3C57A3A21E0A05AD002141DF /* EventType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C57A39D1E0A05AD002141DF /* EventType.cpp */; }; @@ -685,6 +705,80 @@ 3C5399E62994E97400E9125C /* HttpServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = HttpServer.cpp; path = ../../../Source/Http/HttpServer.cpp; sourceTree = ""; }; 3C5399E72994E97400E9125C /* HttpServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HttpServer.h; path = ../../../Source/Http/HttpServer.h; sourceTree = ""; }; 3C5399EA2998BAF000E9125C /* ljson.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ljson.c; path = ../../../Source/3rdParty/colib/ljson.c; sourceTree = ""; }; + 3C53E8E62BE8CF1100039640 /* AbsoluteLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AbsoluteLayout.cpp; sourceTree = ""; }; + 3C53E8E72BE8CF1100039640 /* AbsoluteLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbsoluteLayout.h; sourceTree = ""; }; + 3C53E8E82BE8CF1100039640 /* Align.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Align.h; sourceTree = ""; }; + 3C53E8E92BE8CF1100039640 /* Baseline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Baseline.cpp; sourceTree = ""; }; + 3C53E8EA2BE8CF1100039640 /* Baseline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Baseline.h; sourceTree = ""; }; + 3C53E8EB2BE8CF1100039640 /* BoundAxis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BoundAxis.h; sourceTree = ""; }; + 3C53E8EC2BE8CF1100039640 /* Cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cache.cpp; sourceTree = ""; }; + 3C53E8ED2BE8CF1100039640 /* Cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Cache.h; sourceTree = ""; }; + 3C53E8EE2BE8CF1100039640 /* CalculateLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CalculateLayout.cpp; sourceTree = ""; }; + 3C53E8EF2BE8CF1100039640 /* CalculateLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CalculateLayout.h; sourceTree = ""; }; + 3C53E8F02BE8CF1100039640 /* FlexDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlexDirection.h; sourceTree = ""; }; + 3C53E8F12BE8CF1100039640 /* FlexLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FlexLine.cpp; sourceTree = ""; }; + 3C53E8F22BE8CF1100039640 /* FlexLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlexLine.h; sourceTree = ""; }; + 3C53E8F32BE8CF1100039640 /* PixelGrid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PixelGrid.cpp; sourceTree = ""; }; + 3C53E8F42BE8CF1100039640 /* PixelGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PixelGrid.h; sourceTree = ""; }; + 3C53E8F52BE8CF1100039640 /* SizingMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SizingMode.h; sourceTree = ""; }; + 3C53E8F62BE8CF1100039640 /* TrailingPosition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrailingPosition.h; sourceTree = ""; }; + 3C53E8F82BE8CF1100039640 /* Config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Config.cpp; sourceTree = ""; }; + 3C53E8F92BE8CF1100039640 /* Config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Config.h; sourceTree = ""; }; + 3C53E8FB2BE8CF1100039640 /* AssertFatal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AssertFatal.cpp; sourceTree = ""; }; + 3C53E8FC2BE8CF1100039640 /* AssertFatal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AssertFatal.h; sourceTree = ""; }; + 3C53E8FD2BE8CF1100039640 /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Log.cpp; sourceTree = ""; }; + 3C53E8FE2BE8CF1100039640 /* Log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Log.h; sourceTree = ""; }; + 3C53E9002BE8CF1100039640 /* Align.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Align.h; sourceTree = ""; }; + 3C53E9012BE8CF1100039640 /* Dimension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dimension.h; sourceTree = ""; }; + 3C53E9022BE8CF1100039640 /* Direction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Direction.h; sourceTree = ""; }; + 3C53E9032BE8CF1100039640 /* Display.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Display.h; sourceTree = ""; }; + 3C53E9042BE8CF1100039640 /* Edge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Edge.h; sourceTree = ""; }; + 3C53E9052BE8CF1100039640 /* Errata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Errata.h; sourceTree = ""; }; + 3C53E9062BE8CF1100039640 /* ExperimentalFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExperimentalFeature.h; sourceTree = ""; }; + 3C53E9072BE8CF1100039640 /* FlexDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlexDirection.h; sourceTree = ""; }; + 3C53E9082BE8CF1100039640 /* Gutter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Gutter.h; sourceTree = ""; }; + 3C53E9092BE8CF1100039640 /* Justify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Justify.h; sourceTree = ""; }; + 3C53E90A2BE8CF1100039640 /* LogLevel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LogLevel.h; sourceTree = ""; }; + 3C53E90B2BE8CF1100039640 /* MeasureMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeasureMode.h; sourceTree = ""; }; + 3C53E90C2BE8CF1100039640 /* NodeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NodeType.h; sourceTree = ""; }; + 3C53E90D2BE8CF1100039640 /* Overflow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Overflow.h; sourceTree = ""; }; + 3C53E90E2BE8CF1100039640 /* PhysicalEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhysicalEdge.h; sourceTree = ""; }; + 3C53E90F2BE8CF1100039640 /* PositionType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PositionType.h; sourceTree = ""; }; + 3C53E9102BE8CF1100039640 /* Unit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Unit.h; sourceTree = ""; }; + 3C53E9112BE8CF1100039640 /* Wrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Wrap.h; sourceTree = ""; }; + 3C53E9122BE8CF1100039640 /* YogaEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YogaEnums.h; sourceTree = ""; }; + 3C53E9142BE8CF1100039640 /* event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = event.cpp; sourceTree = ""; }; + 3C53E9152BE8CF1100039640 /* event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = event.h; sourceTree = ""; }; + 3C53E9172BE8CF1100039640 /* CachedMeasurement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedMeasurement.h; sourceTree = ""; }; + 3C53E9182BE8CF1100039640 /* LayoutResults.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LayoutResults.cpp; sourceTree = ""; }; + 3C53E9192BE8CF1100039640 /* LayoutResults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LayoutResults.h; sourceTree = ""; }; + 3C53E91A2BE8CF1100039640 /* Node.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Node.cpp; sourceTree = ""; }; + 3C53E91B2BE8CF1100039640 /* Node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Node.h; sourceTree = ""; }; + 3C53E91D2BE8CF1100039640 /* Comparison.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Comparison.h; sourceTree = ""; }; + 3C53E91E2BE8CF1100039640 /* FloatOptional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FloatOptional.h; sourceTree = ""; }; + 3C53E9202BE8CF1100039640 /* SmallValueBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmallValueBuffer.h; sourceTree = ""; }; + 3C53E9212BE8CF1100039640 /* Style.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Style.h; sourceTree = ""; }; + 3C53E9222BE8CF1100039640 /* StyleLength.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleLength.h; sourceTree = ""; }; + 3C53E9232BE8CF1100039640 /* StyleValueHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleValueHandle.h; sourceTree = ""; }; + 3C53E9242BE8CF1100039640 /* StyleValuePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleValuePool.h; sourceTree = ""; }; + 3C53E9262BE8CF1100039640 /* YGConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGConfig.cpp; sourceTree = ""; }; + 3C53E9272BE8CF1100039640 /* YGConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGConfig.h; sourceTree = ""; }; + 3C53E9282BE8CF1100039640 /* YGEnums.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGEnums.cpp; sourceTree = ""; }; + 3C53E9292BE8CF1100039640 /* YGEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGEnums.h; sourceTree = ""; }; + 3C53E92A2BE8CF1100039640 /* YGMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGMacros.h; sourceTree = ""; }; + 3C53E92B2BE8CF1100039640 /* YGNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGNode.cpp; sourceTree = ""; }; + 3C53E92C2BE8CF1100039640 /* YGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGNode.h; sourceTree = ""; }; + 3C53E92D2BE8CF1100039640 /* YGNodeLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGNodeLayout.cpp; sourceTree = ""; }; + 3C53E92E2BE8CF1100039640 /* YGNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGNodeLayout.h; sourceTree = ""; }; + 3C53E92F2BE8CF1100039640 /* YGNodeStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGNodeStyle.cpp; sourceTree = ""; }; + 3C53E9302BE8CF1100039640 /* YGNodeStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGNodeStyle.h; sourceTree = ""; }; + 3C53E9312BE8CF1100039640 /* YGPixelGrid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGPixelGrid.cpp; sourceTree = ""; }; + 3C53E9322BE8CF1100039640 /* YGPixelGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGPixelGrid.h; sourceTree = ""; }; + 3C53E9332BE8CF1100039640 /* YGValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGValue.cpp; sourceTree = ""; }; + 3C53E9342BE8CF1100039640 /* YGValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGValue.h; sourceTree = ""; }; + 3C53E9352BE8CF1100039640 /* Yoga.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Yoga.h; sourceTree = ""; }; + 3C53E94A2BE9C77D00039640 /* AlignNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AlignNode.cpp; path = ../../../Source/Node/AlignNode.cpp; sourceTree = ""; }; + 3C53E94B2BE9C77D00039640 /* AlignNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AlignNode.h; path = ../../../Source/Node/AlignNode.h; sourceTree = ""; }; 3C54F0D92599CD65003D8F08 /* Value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Value.cpp; path = ../../../Source/Support/Value.cpp; sourceTree = ""; }; 3C57A39B1E0A05AD002141DF /* Event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Event.cpp; path = ../../../Source/Event/Event.cpp; sourceTree = ""; }; 3C57A39C1E0A05AD002141DF /* Event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Event.h; path = ../../../Source/Event/Event.h; sourceTree = ""; }; @@ -1870,6 +1964,7 @@ 3C291D371E0264510098C860 /* 3rdParty */ = { isa = PBXGroup; children = ( + 3C53E9362BE8CF1100039640 /* yoga */, 3C7FA30F2B67AEC6005A8441 /* zlib */, 3C5399E92998BACF00E9125C /* colib */, 3CFEB82E268DA5B6006D9974 /* dragonBones */, @@ -2064,6 +2159,150 @@ name = colib; sourceTree = ""; }; + 3C53E8F72BE8CF1100039640 /* algorithm */ = { + isa = PBXGroup; + children = ( + 3C53E8E62BE8CF1100039640 /* AbsoluteLayout.cpp */, + 3C53E8E72BE8CF1100039640 /* AbsoluteLayout.h */, + 3C53E8E82BE8CF1100039640 /* Align.h */, + 3C53E8E92BE8CF1100039640 /* Baseline.cpp */, + 3C53E8EA2BE8CF1100039640 /* Baseline.h */, + 3C53E8EB2BE8CF1100039640 /* BoundAxis.h */, + 3C53E8EC2BE8CF1100039640 /* Cache.cpp */, + 3C53E8ED2BE8CF1100039640 /* Cache.h */, + 3C53E8EE2BE8CF1100039640 /* CalculateLayout.cpp */, + 3C53E8EF2BE8CF1100039640 /* CalculateLayout.h */, + 3C53E8F02BE8CF1100039640 /* FlexDirection.h */, + 3C53E8F12BE8CF1100039640 /* FlexLine.cpp */, + 3C53E8F22BE8CF1100039640 /* FlexLine.h */, + 3C53E8F32BE8CF1100039640 /* PixelGrid.cpp */, + 3C53E8F42BE8CF1100039640 /* PixelGrid.h */, + 3C53E8F52BE8CF1100039640 /* SizingMode.h */, + 3C53E8F62BE8CF1100039640 /* TrailingPosition.h */, + ); + path = algorithm; + sourceTree = ""; + }; + 3C53E8FA2BE8CF1100039640 /* config */ = { + isa = PBXGroup; + children = ( + 3C53E8F82BE8CF1100039640 /* Config.cpp */, + 3C53E8F92BE8CF1100039640 /* Config.h */, + ); + path = config; + sourceTree = ""; + }; + 3C53E8FF2BE8CF1100039640 /* debug */ = { + isa = PBXGroup; + children = ( + 3C53E8FB2BE8CF1100039640 /* AssertFatal.cpp */, + 3C53E8FC2BE8CF1100039640 /* AssertFatal.h */, + 3C53E8FD2BE8CF1100039640 /* Log.cpp */, + 3C53E8FE2BE8CF1100039640 /* Log.h */, + ); + path = debug; + sourceTree = ""; + }; + 3C53E9132BE8CF1100039640 /* enums */ = { + isa = PBXGroup; + children = ( + 3C53E9002BE8CF1100039640 /* Align.h */, + 3C53E9012BE8CF1100039640 /* Dimension.h */, + 3C53E9022BE8CF1100039640 /* Direction.h */, + 3C53E9032BE8CF1100039640 /* Display.h */, + 3C53E9042BE8CF1100039640 /* Edge.h */, + 3C53E9052BE8CF1100039640 /* Errata.h */, + 3C53E9062BE8CF1100039640 /* ExperimentalFeature.h */, + 3C53E9072BE8CF1100039640 /* FlexDirection.h */, + 3C53E9082BE8CF1100039640 /* Gutter.h */, + 3C53E9092BE8CF1100039640 /* Justify.h */, + 3C53E90A2BE8CF1100039640 /* LogLevel.h */, + 3C53E90B2BE8CF1100039640 /* MeasureMode.h */, + 3C53E90C2BE8CF1100039640 /* NodeType.h */, + 3C53E90D2BE8CF1100039640 /* Overflow.h */, + 3C53E90E2BE8CF1100039640 /* PhysicalEdge.h */, + 3C53E90F2BE8CF1100039640 /* PositionType.h */, + 3C53E9102BE8CF1100039640 /* Unit.h */, + 3C53E9112BE8CF1100039640 /* Wrap.h */, + 3C53E9122BE8CF1100039640 /* YogaEnums.h */, + ); + path = enums; + sourceTree = ""; + }; + 3C53E9162BE8CF1100039640 /* event */ = { + isa = PBXGroup; + children = ( + 3C53E9142BE8CF1100039640 /* event.cpp */, + 3C53E9152BE8CF1100039640 /* event.h */, + ); + path = event; + sourceTree = ""; + }; + 3C53E91C2BE8CF1100039640 /* node */ = { + isa = PBXGroup; + children = ( + 3C53E9172BE8CF1100039640 /* CachedMeasurement.h */, + 3C53E9182BE8CF1100039640 /* LayoutResults.cpp */, + 3C53E9192BE8CF1100039640 /* LayoutResults.h */, + 3C53E91A2BE8CF1100039640 /* Node.cpp */, + 3C53E91B2BE8CF1100039640 /* Node.h */, + ); + path = node; + sourceTree = ""; + }; + 3C53E91F2BE8CF1100039640 /* numeric */ = { + isa = PBXGroup; + children = ( + 3C53E91D2BE8CF1100039640 /* Comparison.h */, + 3C53E91E2BE8CF1100039640 /* FloatOptional.h */, + ); + path = numeric; + sourceTree = ""; + }; + 3C53E9252BE8CF1100039640 /* style */ = { + isa = PBXGroup; + children = ( + 3C53E9202BE8CF1100039640 /* SmallValueBuffer.h */, + 3C53E9212BE8CF1100039640 /* Style.h */, + 3C53E9222BE8CF1100039640 /* StyleLength.h */, + 3C53E9232BE8CF1100039640 /* StyleValueHandle.h */, + 3C53E9242BE8CF1100039640 /* StyleValuePool.h */, + ); + path = style; + sourceTree = ""; + }; + 3C53E9362BE8CF1100039640 /* yoga */ = { + isa = PBXGroup; + children = ( + 3C53E8F72BE8CF1100039640 /* algorithm */, + 3C53E8FA2BE8CF1100039640 /* config */, + 3C53E8FF2BE8CF1100039640 /* debug */, + 3C53E9132BE8CF1100039640 /* enums */, + 3C53E9162BE8CF1100039640 /* event */, + 3C53E91C2BE8CF1100039640 /* node */, + 3C53E91F2BE8CF1100039640 /* numeric */, + 3C53E9252BE8CF1100039640 /* style */, + 3C53E9262BE8CF1100039640 /* YGConfig.cpp */, + 3C53E9272BE8CF1100039640 /* YGConfig.h */, + 3C53E9282BE8CF1100039640 /* YGEnums.cpp */, + 3C53E9292BE8CF1100039640 /* YGEnums.h */, + 3C53E92A2BE8CF1100039640 /* YGMacros.h */, + 3C53E92B2BE8CF1100039640 /* YGNode.cpp */, + 3C53E92C2BE8CF1100039640 /* YGNode.h */, + 3C53E92D2BE8CF1100039640 /* YGNodeLayout.cpp */, + 3C53E92E2BE8CF1100039640 /* YGNodeLayout.h */, + 3C53E92F2BE8CF1100039640 /* YGNodeStyle.cpp */, + 3C53E9302BE8CF1100039640 /* YGNodeStyle.h */, + 3C53E9312BE8CF1100039640 /* YGPixelGrid.cpp */, + 3C53E9322BE8CF1100039640 /* YGPixelGrid.h */, + 3C53E9332BE8CF1100039640 /* YGValue.cpp */, + 3C53E9342BE8CF1100039640 /* YGValue.h */, + 3C53E9352BE8CF1100039640 /* Yoga.h */, + ); + name = yoga; + path = ../../../Source/3rdParty/yoga; + sourceTree = ""; + }; 3C57A39A1E0A059A002141DF /* Event */ = { isa = PBXGroup; children = ( @@ -2807,6 +3046,8 @@ 3CCBBA381E14B50B00C0416B /* Node */ = { isa = PBXGroup; children = ( + 3C53E94A2BE9C77D00039640 /* AlignNode.cpp */, + 3C53E94B2BE9C77D00039640 /* AlignNode.h */, 3CCB0C771E6DB0590088C118 /* ClipNode.cpp */, 3CCB0C781E6DB0590088C118 /* ClipNode.h */, 3CFEB82B268DA599006D9974 /* DragonBone.cpp */, @@ -3344,6 +3585,7 @@ 3C6E7F31272A30E700E9F115 /* sndrender.cpp in Sources */, 3C6E7F32272A30E700E9F115 /* soloud_sfxr.cpp in Sources */, 3C6E7F30272A30E700E9F115 /* soloud_ay.cpp in Sources */, + 3C53E9422BE8CF1100039640 /* Node.cpp in Sources */, 3CA77EDF2AE665C100B7BADA /* RayCastOutput.cpp in Sources */, 3C7708331E08CB4300B38C2A /* LuaCode.cpp in Sources */, 3CA77EF02AE665C100B7BADA /* AABB.cpp in Sources */, @@ -3362,6 +3604,7 @@ 3CEDF77E1E839DE6008839A3 /* ParticleCache.cpp in Sources */, 3CFEA11B285B857B002D106F /* m3_api_wasi.c in Sources */, 3CFEB860268DA65C006D9974 /* TextureAtlasData.cpp in Sources */, + 3C53E93B2BE8CF1100039640 /* FlexLine.cpp in Sources */, 3CFEBD65268DA6D5006D9974 /* Bone.cpp in Sources */, 3C6E7F11272A30E700E9F115 /* soloud_waveshaperfilter.cpp in Sources */, 3C5399EB2998BAF000E9125C /* ljson.c in Sources */, @@ -3371,15 +3614,18 @@ 3CF10EF12874AE7E00940268 /* Atlas.cpp in Sources */, 3CA77EEB2AE665C100B7BADA /* Sweep.cpp in Sources */, 3C6E7F2A272A30E700E9F115 /* dr_impl.cpp in Sources */, + 3C53E9452BE8CF1100039640 /* YGNode.cpp in Sources */, 3CFEA11A285B857B002D106F /* m3_core.c in Sources */, 3C0D31192456C7F6009C592B /* AtlasCache.cpp in Sources */, 3C6E7F34272A30E700E9F115 /* tts.cpp in Sources */, 3C9A7D731E5428A200205094 /* Label.cpp in Sources */, 3C1A3AFA272A850F003A2829 /* lauxlib.c in Sources */, + 3C53E9492BE8CF1100039640 /* YGValue.cpp in Sources */, 3C6E7F19272A30E700E9F115 /* soloud_audiosource.cpp in Sources */, 3C1A3B0A272A850F003A2829 /* lmathlib.c in Sources */, 3CF10ECB2874AE7E00940268 /* AnimationStateData.cpp in Sources */, 3CA77EF92AE665C100B7BADA /* GearJointConf.cpp in Sources */, + 3C53E93D2BE8CF1100039640 /* Config.cpp in Sources */, 3C0EBE361E2E1D840066450A /* ShaderCache.cpp in Sources */, 3CA77EFE2AE665C100B7BADA /* WorldShape.cpp in Sources */, 3C6E7F0E272A30E700E9F115 /* soloud_flangerfilter.cpp in Sources */, @@ -3400,6 +3646,7 @@ 3C1984792100656800B6F370 /* PlatformCamera.cpp in Sources */, 3CF10EC92874AE7E00940268 /* TextureLoader.cpp in Sources */, 3C6DA2051EAEF19E006EEE32 /* Model.cpp in Sources */, + 3C53E94C2BE9C77D00039640 /* AlignNode.cpp in Sources */, 3CE9D7F91E7FC2A6003AAECB /* PhysicsWorld.cpp in Sources */, 3C0D399D1E1BA603007FEFC3 /* Common.cpp in Sources */, 3CFEA12C285B860B002D106F /* WasmRuntime.cpp in Sources */, @@ -3448,6 +3695,7 @@ 3C40555320FD93AA0057B0E4 /* Define.cpp in Sources */, 3C9CCD042689BDD00086757D /* format.cpp in Sources */, 3CD497FE1E7EF3DF00C06CA2 /* FrameCache.cpp in Sources */, + 3C53E9412BE8CF1100039640 /* LayoutResults.cpp in Sources */, 3CB487171E16398700D51749 /* tolua_push.cpp in Sources */, 3CA77ED72AE665C100B7BADA /* MovementConf.cpp in Sources */, 3CFEBD68268DA6D5006D9974 /* Armature.cpp in Sources */, @@ -3466,6 +3714,7 @@ 3C6E7F13272A30E700E9F115 /* soloud_fft_lut.cpp in Sources */, 3C1A3AF5272A850F003A2829 /* lctype.c in Sources */, 3C5399E82994E97400E9125C /* HttpServer.cpp in Sources */, + 3C53E93C2BE8CF1100039640 /* PixelGrid.cpp in Sources */, 3CFEA122285B857B002D106F /* m3_info.c in Sources */, 3CFEB857268DA65C006D9974 /* ArmatureData.cpp in Sources */, 3CF10EEB2874AE7E00940268 /* IkConstraintTimeline.cpp in Sources */, @@ -3477,6 +3726,7 @@ 3CE9D7FC1E7FE99D003AAECB /* Dictionary.cpp in Sources */, 3C6E7F16272A30E700E9F115 /* soloud_fader.cpp in Sources */, 3CF10ECA2874AE7E00940268 /* TransformConstraint.cpp in Sources */, + 3C53E93F2BE8CF1100039640 /* Log.cpp in Sources */, 3C1A3AF6272A850F003A2829 /* loslib.c in Sources */, 3C6E7F39272A30E700E9F115 /* soloud_speech.cpp in Sources */, 3CE9D7F51E7FC2A6003AAECB /* BodyDef.cpp in Sources */, @@ -3556,6 +3806,7 @@ 3CFEA11F285B857B002D106F /* m3_env.c in Sources */, 3C1A3AEB272A850F003A2829 /* lundump.c in Sources */, 3CF10EDE2874AE7E00940268 /* ShearTimeline.cpp in Sources */, + 3C53E9402BE8CF1100039640 /* event.cpp in Sources */, 3CF10EF52874AE7E00940268 /* Event.cpp in Sources */, 3C6E7F18272A30E700E9F115 /* soloud.cpp in Sources */, 3C7FA32E2B67AEE0005A8441 /* gzlib.c in Sources */, @@ -3572,7 +3823,9 @@ 3CFEA124285B857B002D106F /* m3_parse.c in Sources */, 3CF10EEC2874AE7E00940268 /* Log.cpp in Sources */, 3C1A3AEC272A850F003A2829 /* ltm.c in Sources */, + 3C53E9442BE8CF1100039640 /* YGEnums.cpp in Sources */, 3C54F0DA2599CD65003D8F08 /* Value.cpp in Sources */, + 3C53E93E2BE8CF1100039640 /* AssertFatal.cpp in Sources */, 3CA77EE22AE665C100B7BADA /* MotorJointConf.cpp in Sources */, 3CF10EFF2874AE7E00940268 /* AnimationState.cpp in Sources */, 3CA77F012AE665C100B7BADA /* PolygonShapeConf.cpp in Sources */, @@ -3620,6 +3873,7 @@ 3C47CF1128A356B40011F3E1 /* Cache.cpp in Sources */, 3C6E7F15272A30E700E9F115 /* soloud_bus.cpp in Sources */, 3CFEBD66268DA6D5006D9974 /* Constraint.cpp in Sources */, + 3C53E93A2BE8CF1100039640 /* CalculateLayout.cpp in Sources */, 3CD497FB1E7EE52300C06CA2 /* ModelCache.cpp in Sources */, 3CFEB871268DA694006D9974 /* EventObject.cpp in Sources */, 3CA77F062AE665C100B7BADA /* Velocity.cpp in Sources */, @@ -3648,7 +3902,9 @@ 3C46E85D2614227300DC9D4F /* Camera.cpp in Sources */, 3CF10EC62874AE7E00940268 /* CurveTimeline.cpp in Sources */, 3CA77EDB2AE665C100B7BADA /* StepConf.cpp in Sources */, + 3C53E9432BE8CF1100039640 /* YGConfig.cpp in Sources */, 3C7FA32C2B67AEE0005A8441 /* inffast.c in Sources */, + 3C53E9462BE8CF1100039640 /* YGNodeLayout.cpp in Sources */, 3C46E83126140A8F00DC9D4F /* Exception.cpp in Sources */, 3CA77F082AE665C100B7BADA /* ChainShapeConf.cpp in Sources */, 3CA77F192AE665C100B7BADA /* LimitState.cpp in Sources */, @@ -3656,6 +3912,7 @@ 3CF10EDB2874AE7E00940268 /* DrawOrderTimeline.cpp in Sources */, 3C46E85E2614227300DC9D4F /* View.cpp in Sources */, 3CA77EFB2AE665C100B7BADA /* Body.cpp in Sources */, + 3C53E9472BE8CF1100039640 /* YGNodeStyle.cpp in Sources */, 3C6E7F42272A30E700E9F115 /* soloud_openmpt.cpp in Sources */, 3C1A3AF8272A850F003A2829 /* ltable.c in Sources */, 3C46E83526140A8F00DC9D4F /* Backup.cpp in Sources */, @@ -3720,16 +3977,20 @@ 3C42B09720FEC57C00186F9C /* AINode.cpp in Sources */, 3CFEB877268DA6A3006D9974 /* DragonBones.cpp in Sources */, 3C6E7F24272A30E700E9F115 /* soloud_misc.cpp in Sources */, + 3C53E9392BE8CF1100039640 /* Cache.cpp in Sources */, 3CF10EE32874AE7E00940268 /* Skin.cpp in Sources */, 3CA77F172AE665C100B7BADA /* StatsResource.cpp in Sources */, 3CA77F0E2AE665C100B7BADA /* CodeDumper.cpp in Sources */, + 3C53E9482BE8CF1100039640 /* YGPixelGrid.cpp in Sources */, 3C46E8602614227300DC9D4F /* Application.cpp in Sources */, 3CFEBDA2268DB403006D9974 /* DragonBoneCache.cpp in Sources */, 3CA77F022AE665C100B7BADA /* PulleyJointConf.cpp in Sources */, 3CCB0C791E6DB0590088C118 /* ClipNode.cpp in Sources */, 3CA77EFC2AE665C100B7BADA /* WheelJointConf.cpp in Sources */, + 3C53E9382BE8CF1100039640 /* Baseline.cpp in Sources */, 3C1A3B03272A850F003A2829 /* lcorolib.c in Sources */, 3C6E7F1B272A30E700E9F115 /* soloud_core_setters.cpp in Sources */, + 3C53E9372BE8CF1100039640 /* AbsoluteLayout.cpp in Sources */, 3CF10EE92874AE7E00940268 /* TransformConstraintTimeline.cpp in Sources */, 3C6E7F4B272A389200E9F115 /* sqlite3.c in Sources */, 3C6E7F0F272A30E700E9F115 /* soloud_freeverbfilter.cpp in Sources */, diff --git a/Project/macOS/Dora/Info.plist b/Project/macOS/Dora/Info.plist index 34114fc96..5cbb30944 100644 --- a/Project/macOS/Dora/Info.plist +++ b/Project/macOS/Dora/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.22 + 1.3.23 CFBundleSignature ???? CFBundleVersion - 54 + 55 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/Source/3rdParty/yoga/LICENSE b/Source/3rdParty/yoga/LICENSE new file mode 100644 index 000000000..b96dcb048 --- /dev/null +++ b/Source/3rdParty/yoga/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Facebook, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Source/3rdParty/yoga/YGConfig.cpp b/Source/3rdParty/yoga/YGConfig.cpp new file mode 100644 index 000000000..55dcb587d --- /dev/null +++ b/Source/3rdParty/yoga/YGConfig.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/Yoga.h" +#include "yoga/debug/AssertFatal.h" +#include "yoga/debug/Log.h" + +using namespace facebook; +using namespace facebook::yoga; + +YGConfigRef YGConfigNew(void) { + return new yoga::Config(getDefaultLogger()); +} + +void YGConfigFree(const YGConfigRef config) { + delete resolveRef(config); +} + +YGConfigConstRef YGConfigGetDefault() { + return &yoga::Config::getDefault(); +} + +void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled) { + resolveRef(config)->setUseWebDefaults(enabled); +} + +bool YGConfigGetUseWebDefaults(const YGConfigConstRef config) { + return resolveRef(config)->useWebDefaults(); +} + +void YGConfigSetPointScaleFactor( + const YGConfigRef config, + const float pixelsInPoint) { + yoga::assertFatalWithConfig( + resolveRef(config), + pixelsInPoint >= 0.0f, + "Scale factor should not be less than zero"); + + resolveRef(config)->setPointScaleFactor(pixelsInPoint); +} + +float YGConfigGetPointScaleFactor(const YGConfigConstRef config) { + return resolveRef(config)->getPointScaleFactor(); +} + +void YGConfigSetErrata(YGConfigRef config, YGErrata errata) { + resolveRef(config)->setErrata(scopedEnum(errata)); +} + +YGErrata YGConfigGetErrata(YGConfigConstRef config) { + return unscopedEnum(resolveRef(config)->getErrata()); +} + +void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) { + if (logger != nullptr) { + resolveRef(config)->setLogger(logger); + } else { + resolveRef(config)->setLogger(getDefaultLogger()); + } +} + +void YGConfigSetContext(const YGConfigRef config, void* context) { + resolveRef(config)->setContext(context); +} + +void* YGConfigGetContext(const YGConfigConstRef config) { + return resolveRef(config)->getContext(); +} + +void YGConfigSetExperimentalFeatureEnabled( + const YGConfigRef config, + const YGExperimentalFeature feature, + const bool enabled) { + resolveRef(config)->setExperimentalFeatureEnabled( + scopedEnum(feature), enabled); +} + +bool YGConfigIsExperimentalFeatureEnabled( + const YGConfigConstRef config, + const YGExperimentalFeature feature) { + return resolveRef(config)->isExperimentalFeatureEnabled(scopedEnum(feature)); +} + +void YGConfigSetCloneNodeFunc( + const YGConfigRef config, + const YGCloneNodeFunc callback) { + resolveRef(config)->setCloneNodeCallback(callback); +} diff --git a/Source/3rdParty/yoga/YGConfig.h b/Source/3rdParty/yoga/YGConfig.h new file mode 100644 index 000000000..4ef7bb581 --- /dev/null +++ b/Source/3rdParty/yoga/YGConfig.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include "yoga/YGEnums.h" +#include "yoga/YGMacros.h" + +YG_EXTERN_C_BEGIN + +typedef struct YGNode* YGNodeRef; +typedef const struct YGNode* YGNodeConstRef; + +/** + * Handle to a mutable Yoga configuration. + */ +typedef struct YGConfig* YGConfigRef; + +/** + * Handle to an immutable Yoga configuration. + */ +typedef const struct YGConfig* YGConfigConstRef; + +/** + * Allocates a set of configuration options. The configuration may be applied to + * multiple nodes (i.e. a single global config), or can be applied more + * granularly per-node. + */ +YG_EXPORT YGConfigRef YGConfigNew(void); + +/** + * Frees the associated Yoga configuration. + */ +YG_EXPORT void YGConfigFree(YGConfigRef config); + +/** + * Returns the default config values set by Yoga. + */ +YG_EXPORT YGConfigConstRef YGConfigGetDefault(void); + +/** + * Yoga by default creates new nodes with style defaults different from flexbox + * on web (e.g. `YGFlexDirectionColumn` and `YGPositionRelative`). + * `UseWebDefaults` instructs Yoga to instead use a default style consistent + * with the web. + */ +YG_EXPORT void YGConfigSetUseWebDefaults(YGConfigRef config, bool enabled); + +/** + * Whether the configuration is set to use web defaults. + */ +YG_EXPORT bool YGConfigGetUseWebDefaults(YGConfigConstRef config); + +/** + * Yoga will by default round final layout positions and dimensions to the + * nearst point. `pointScaleFactor` controls the density of the grid used for + * layout rounding (e.g. to round to the closest display pixel). + * + * May be set to 0.0f to avoid rounding the layout results. + */ +YG_EXPORT void YGConfigSetPointScaleFactor( + YGConfigRef config, + float pixelsInPoint); + +/** + * Get the currently set point scale factor. + */ +YG_EXPORT float YGConfigGetPointScaleFactor(YGConfigConstRef config); + +/** + * Configures how Yoga balances W3C conformance vs compatibility with layouts + * created against earlier versions of Yoga. + * + * By default Yoga will prioritize W3C conformance. `Errata` may be set to ask + * Yoga to produce specific incorrect behaviors. E.g. `YGConfigSetErrata(config, + * YGErrataStretchFlexBasis)`. + * + * YGErrata is a bitmask, and multiple errata may be set at once. Predefined + * constants exist for convenience: + * 1. YGErrataNone: No errata + * 2. YGErrataClassic: Match layout behaviors of Yoga 1.x + * 3. YGErrataAll: Match layout behaviors of Yoga 1.x, including + * `UseLegacyStretchBehaviour` + */ +YG_EXPORT void YGConfigSetErrata(YGConfigRef config, YGErrata errata); + +/** + * Get the currently set errata. + */ +YG_EXPORT YGErrata YGConfigGetErrata(YGConfigConstRef config); + +/** + * Function pointer type for YGConfigSetLogger. + */ +typedef int (*YGLogger)( + YGConfigConstRef config, + YGNodeConstRef node, + YGLogLevel level, + const char* format, + va_list args); + +/** + * Set a custom log function for to use when logging diagnostics or fatal. + * errors. + */ +YG_EXPORT void YGConfigSetLogger(YGConfigRef config, YGLogger logger); + +/** + * Sets an arbitrary context pointer on the config which may be read from during + * callbacks. + */ +YG_EXPORT void YGConfigSetContext(YGConfigRef config, void* context); + +/** + * Gets the currently set context. + */ +YG_EXPORT void* YGConfigGetContext(YGConfigConstRef config); + +/** + * Function pointer type for YGConfigSetCloneNodeFunc. + */ +typedef YGNodeRef (*YGCloneNodeFunc)( + YGNodeConstRef oldNode, + YGNodeConstRef owner, + size_t childIndex); + +/** + * Enable an experimental/unsupported feature in Yoga. + */ +YG_EXPORT void YGConfigSetExperimentalFeatureEnabled( + YGConfigRef config, + YGExperimentalFeature feature, + bool enabled); + +/** + * Whether an experimental feature is set. + */ +YG_EXPORT bool YGConfigIsExperimentalFeatureEnabled( + YGConfigConstRef config, + YGExperimentalFeature feature); + +/** + * Sets a callback, called during layout, to create a new mutable Yoga node if + * Yoga must write to it and its owner is not its parent observed during layout. + */ +YG_EXPORT void YGConfigSetCloneNodeFunc( + YGConfigRef config, + YGCloneNodeFunc callback); + +YG_EXTERN_C_END diff --git a/Source/3rdParty/yoga/YGEnums.cpp b/Source/3rdParty/yoga/YGEnums.cpp new file mode 100644 index 000000000..bc1b54834 --- /dev/null +++ b/Source/3rdParty/yoga/YGEnums.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#include "yoga/YGEnums.h" + +const char* YGAlignToString(const YGAlign value) { + switch (value) { + case YGAlignAuto: + return "auto"; + case YGAlignFlexStart: + return "flex-start"; + case YGAlignCenter: + return "center"; + case YGAlignFlexEnd: + return "flex-end"; + case YGAlignStretch: + return "stretch"; + case YGAlignBaseline: + return "baseline"; + case YGAlignSpaceBetween: + return "space-between"; + case YGAlignSpaceAround: + return "space-around"; + case YGAlignSpaceEvenly: + return "space-evenly"; + } + return "unknown"; +} + +const char* YGDimensionToString(const YGDimension value) { + switch (value) { + case YGDimensionWidth: + return "width"; + case YGDimensionHeight: + return "height"; + } + return "unknown"; +} + +const char* YGDirectionToString(const YGDirection value) { + switch (value) { + case YGDirectionInherit: + return "inherit"; + case YGDirectionLTR: + return "ltr"; + case YGDirectionRTL: + return "rtl"; + } + return "unknown"; +} + +const char* YGDisplayToString(const YGDisplay value) { + switch (value) { + case YGDisplayFlex: + return "flex"; + case YGDisplayNone: + return "none"; + } + return "unknown"; +} + +const char* YGEdgeToString(const YGEdge value) { + switch (value) { + case YGEdgeLeft: + return "left"; + case YGEdgeTop: + return "top"; + case YGEdgeRight: + return "right"; + case YGEdgeBottom: + return "bottom"; + case YGEdgeStart: + return "start"; + case YGEdgeEnd: + return "end"; + case YGEdgeHorizontal: + return "horizontal"; + case YGEdgeVertical: + return "vertical"; + case YGEdgeAll: + return "all"; + } + return "unknown"; +} + +const char* YGErrataToString(const YGErrata value) { + switch (value) { + case YGErrataNone: + return "none"; + case YGErrataStretchFlexBasis: + return "stretch-flex-basis"; + case YGErrataAbsolutePositioningIncorrect: + return "absolute-positioning-incorrect"; + case YGErrataAbsolutePercentAgainstInnerSize: + return "absolute-percent-against-inner-size"; + case YGErrataAll: + return "all"; + case YGErrataClassic: + return "classic"; + } + return "unknown"; +} + +const char* YGExperimentalFeatureToString(const YGExperimentalFeature value) { + switch (value) { + case YGExperimentalFeatureWebFlexBasis: + return "web-flex-basis"; + } + return "unknown"; +} + +const char* YGFlexDirectionToString(const YGFlexDirection value) { + switch (value) { + case YGFlexDirectionColumn: + return "column"; + case YGFlexDirectionColumnReverse: + return "column-reverse"; + case YGFlexDirectionRow: + return "row"; + case YGFlexDirectionRowReverse: + return "row-reverse"; + } + return "unknown"; +} + +const char* YGGutterToString(const YGGutter value) { + switch (value) { + case YGGutterColumn: + return "column"; + case YGGutterRow: + return "row"; + case YGGutterAll: + return "all"; + } + return "unknown"; +} + +const char* YGJustifyToString(const YGJustify value) { + switch (value) { + case YGJustifyFlexStart: + return "flex-start"; + case YGJustifyCenter: + return "center"; + case YGJustifyFlexEnd: + return "flex-end"; + case YGJustifySpaceBetween: + return "space-between"; + case YGJustifySpaceAround: + return "space-around"; + case YGJustifySpaceEvenly: + return "space-evenly"; + } + return "unknown"; +} + +const char* YGLogLevelToString(const YGLogLevel value) { + switch (value) { + case YGLogLevelError: + return "error"; + case YGLogLevelWarn: + return "warn"; + case YGLogLevelInfo: + return "info"; + case YGLogLevelDebug: + return "debug"; + case YGLogLevelVerbose: + return "verbose"; + case YGLogLevelFatal: + return "fatal"; + } + return "unknown"; +} + +const char* YGMeasureModeToString(const YGMeasureMode value) { + switch (value) { + case YGMeasureModeUndefined: + return "undefined"; + case YGMeasureModeExactly: + return "exactly"; + case YGMeasureModeAtMost: + return "at-most"; + } + return "unknown"; +} + +const char* YGNodeTypeToString(const YGNodeType value) { + switch (value) { + case YGNodeTypeDefault: + return "default"; + case YGNodeTypeText: + return "text"; + } + return "unknown"; +} + +const char* YGOverflowToString(const YGOverflow value) { + switch (value) { + case YGOverflowVisible: + return "visible"; + case YGOverflowHidden: + return "hidden"; + case YGOverflowScroll: + return "scroll"; + } + return "unknown"; +} + +const char* YGPositionTypeToString(const YGPositionType value) { + switch (value) { + case YGPositionTypeStatic: + return "static"; + case YGPositionTypeRelative: + return "relative"; + case YGPositionTypeAbsolute: + return "absolute"; + } + return "unknown"; +} + +const char* YGUnitToString(const YGUnit value) { + switch (value) { + case YGUnitUndefined: + return "undefined"; + case YGUnitPoint: + return "point"; + case YGUnitPercent: + return "percent"; + case YGUnitAuto: + return "auto"; + } + return "unknown"; +} + +const char* YGWrapToString(const YGWrap value) { + switch (value) { + case YGWrapNoWrap: + return "no-wrap"; + case YGWrapWrap: + return "wrap"; + case YGWrapWrapReverse: + return "wrap-reverse"; + } + return "unknown"; +} diff --git a/Source/3rdParty/yoga/YGEnums.h b/Source/3rdParty/yoga/YGEnums.h new file mode 100644 index 000000000..4295a41c5 --- /dev/null +++ b/Source/3rdParty/yoga/YGEnums.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once +#include "yoga/YGMacros.h" + +YG_EXTERN_C_BEGIN + +YG_ENUM_DECL( + YGAlign, + YGAlignAuto, + YGAlignFlexStart, + YGAlignCenter, + YGAlignFlexEnd, + YGAlignStretch, + YGAlignBaseline, + YGAlignSpaceBetween, + YGAlignSpaceAround, + YGAlignSpaceEvenly) + +YG_ENUM_DECL( + YGDimension, + YGDimensionWidth, + YGDimensionHeight) + +YG_ENUM_DECL( + YGDirection, + YGDirectionInherit, + YGDirectionLTR, + YGDirectionRTL) + +YG_ENUM_DECL( + YGDisplay, + YGDisplayFlex, + YGDisplayNone) + +YG_ENUM_DECL( + YGEdge, + YGEdgeLeft, + YGEdgeTop, + YGEdgeRight, + YGEdgeBottom, + YGEdgeStart, + YGEdgeEnd, + YGEdgeHorizontal, + YGEdgeVertical, + YGEdgeAll) + +YG_ENUM_DECL( + YGErrata, + YGErrataNone = 0, + YGErrataStretchFlexBasis = 1, + YGErrataAbsolutePositioningIncorrect = 2, + YGErrataAbsolutePercentAgainstInnerSize = 4, + YGErrataAll = 2147483647, + YGErrataClassic = 2147483646) +YG_DEFINE_ENUM_FLAG_OPERATORS(YGErrata) + +YG_ENUM_DECL( + YGExperimentalFeature, + YGExperimentalFeatureWebFlexBasis) + +YG_ENUM_DECL( + YGFlexDirection, + YGFlexDirectionColumn, + YGFlexDirectionColumnReverse, + YGFlexDirectionRow, + YGFlexDirectionRowReverse) + +YG_ENUM_DECL( + YGGutter, + YGGutterColumn, + YGGutterRow, + YGGutterAll) + +YG_ENUM_DECL( + YGJustify, + YGJustifyFlexStart, + YGJustifyCenter, + YGJustifyFlexEnd, + YGJustifySpaceBetween, + YGJustifySpaceAround, + YGJustifySpaceEvenly) + +YG_ENUM_DECL( + YGLogLevel, + YGLogLevelError, + YGLogLevelWarn, + YGLogLevelInfo, + YGLogLevelDebug, + YGLogLevelVerbose, + YGLogLevelFatal) + +YG_ENUM_DECL( + YGMeasureMode, + YGMeasureModeUndefined, + YGMeasureModeExactly, + YGMeasureModeAtMost) + +YG_ENUM_DECL( + YGNodeType, + YGNodeTypeDefault, + YGNodeTypeText) + +YG_ENUM_DECL( + YGOverflow, + YGOverflowVisible, + YGOverflowHidden, + YGOverflowScroll) + +YG_ENUM_DECL( + YGPositionType, + YGPositionTypeStatic, + YGPositionTypeRelative, + YGPositionTypeAbsolute) + +YG_ENUM_DECL( + YGUnit, + YGUnitUndefined, + YGUnitPoint, + YGUnitPercent, + YGUnitAuto) + +YG_ENUM_DECL( + YGWrap, + YGWrapNoWrap, + YGWrapWrap, + YGWrapWrapReverse) + +YG_EXTERN_C_END diff --git a/Source/3rdParty/yoga/YGMacros.h b/Source/3rdParty/yoga/YGMacros.h new file mode 100644 index 000000000..6d1becce7 --- /dev/null +++ b/Source/3rdParty/yoga/YGMacros.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#ifdef __cplusplus +#include +#endif + +#ifdef __cplusplus +#define YG_EXTERN_C_BEGIN extern "C" { +#define YG_EXTERN_C_END } +#else +#define YG_EXTERN_C_BEGIN +#define YG_EXTERN_C_END +#endif + +#if defined(__cplusplus) +#define YG_DEPRECATED(message) [[deprecated(message)]] +#elif defined(_MSC_VER) +#define YG_DEPRECATED(message) __declspec(deprecated(message)) +#else +#define YG_DEPRECATED(message) __attribute__((deprecated(message))) +#endif + +#ifdef _WINDLL +#define YG_EXPORT __declspec(dllexport) +#elif !defined(_MSC_VER) +#define YG_EXPORT __attribute__((visibility("default"))) +#else +#define YG_EXPORT +#endif + +#ifdef NS_ENUM +// Cannot use NSInteger as NSInteger has a different size than int (which is the +// default type of a enum). Therefor when linking the Yoga C library into obj-c +// the header is a mismatch for the Yoga ABI. +#define YG_ENUM_BEGIN(name) NS_ENUM(int, name) +#define YG_ENUM_END(name) +#else +#define YG_ENUM_BEGIN(name) enum name +#define YG_ENUM_END(name) name +#endif + +#ifdef __cplusplus +#define YG_DEFINE_ENUM_FLAG_OPERATORS(name) \ + extern "C++" { \ + constexpr name operator~(name a) { \ + return static_cast( \ + ~static_cast::type>(a)); \ + } \ + constexpr name operator|(name a, name b) { \ + return static_cast( \ + static_cast::type>(a) | \ + static_cast::type>(b)); \ + } \ + constexpr name operator&(name a, name b) { \ + return static_cast( \ + static_cast::type>(a) & \ + static_cast::type>(b)); \ + } \ + constexpr name operator^(name a, name b) { \ + return static_cast( \ + static_cast::type>(a) ^ \ + static_cast::type>(b)); \ + } \ + inline name& operator|=(name& a, name b) { \ + return reinterpret_cast( \ + reinterpret_cast::type&>(a) |= \ + static_cast::type>(b)); \ + } \ + inline name& operator&=(name& a, name b) { \ + return reinterpret_cast( \ + reinterpret_cast::type&>(a) &= \ + static_cast::type>(b)); \ + } \ + inline name& operator^=(name& a, name b) { \ + return reinterpret_cast( \ + reinterpret_cast::type&>(a) ^= \ + static_cast::type>(b)); \ + } \ + } +#else +#define YG_DEFINE_ENUM_FLAG_OPERATORS(name) +#endif + +#define YG_ENUM_DECL(NAME, ...) \ + typedef YG_ENUM_BEGIN(NAME){__VA_ARGS__} YG_ENUM_END(NAME); \ + YG_EXPORT const char* NAME##ToString(NAME); diff --git a/Source/3rdParty/yoga/YGNode.cpp b/Source/3rdParty/yoga/YGNode.cpp new file mode 100644 index 000000000..13ceb5d1d --- /dev/null +++ b/Source/3rdParty/yoga/YGNode.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/Yoga.h" + +#include "yoga/algorithm/Cache.h" +#include "yoga/algorithm/CalculateLayout.h" +#include "yoga/debug/AssertFatal.h" +#include "yoga/debug/Log.h" +#include "yoga/event/event.h" +#include "yoga/node/Node.h" + +using namespace facebook; +using namespace facebook::yoga; + +YGNodeRef YGNodeNew(void) { + return YGNodeNewWithConfig(YGConfigGetDefault()); +} + +YGNodeRef YGNodeNewWithConfig(const YGConfigConstRef config) { + auto* node = new yoga::Node{resolveRef(config)}; + yoga::assertFatal( + config != nullptr, "Tried to construct YGNode with null config"); + Event::publish(node, {config}); + + return node; +} + +YGNodeRef YGNodeClone(YGNodeConstRef oldNodeRef) { + auto oldNode = resolveRef(oldNodeRef); + const auto node = new yoga::Node(*oldNode); + Event::publish(node, {node->getConfig()}); + node->setOwner(nullptr); + return node; +} + +void YGNodeFree(const YGNodeRef nodeRef) { + const auto node = resolveRef(nodeRef); + + if (auto owner = node->getOwner()) { + owner->removeChild(node); + node->setOwner(nullptr); + } + + const size_t childCount = node->getChildCount(); + for (size_t i = 0; i < childCount; i++) { + auto child = node->getChild(i); + child->setOwner(nullptr); + } + + node->clearChildren(); + + Event::publish(node, {YGNodeGetConfig(node)}); + delete resolveRef(node); +} + +void YGNodeFreeRecursive(YGNodeRef rootRef) { + const auto root = resolveRef(rootRef); + + size_t skipped = 0; + while (root->getChildCount() > skipped) { + const auto child = root->getChild(skipped); + if (child->getOwner() != root) { + // Don't free shared nodes that we don't own. + skipped += 1; + } else { + YGNodeRemoveChild(root, child); + YGNodeFreeRecursive(child); + } + } + YGNodeFree(root); +} + +void YGNodeFinalize(const YGNodeRef node) { + Event::publish(node, {YGNodeGetConfig(node)}); + delete resolveRef(node); +} + +void YGNodeReset(YGNodeRef node) { + resolveRef(node)->reset(); +} + +void YGNodeCalculateLayout( + const YGNodeRef node, + const float ownerWidth, + const float ownerHeight, + const YGDirection ownerDirection) { + yoga::calculateLayout( + resolveRef(node), ownerWidth, ownerHeight, scopedEnum(ownerDirection)); +} + +bool YGNodeGetHasNewLayout(YGNodeConstRef node) { + return resolveRef(node)->getHasNewLayout(); +} + +void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout) { + resolveRef(node)->setHasNewLayout(hasNewLayout); +} + +bool YGNodeIsDirty(YGNodeConstRef node) { + return resolveRef(node)->isDirty(); +} + +void YGNodeMarkDirty(const YGNodeRef nodeRef) { + const auto node = resolveRef(nodeRef); + + yoga::assertFatalWithNode( + node, + node->hasMeasureFunc(), + "Only leaf nodes with custom measure functions " + "should manually mark themselves as dirty"); + + node->markDirtyAndPropagate(); +} + +void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc) { + resolveRef(node)->setDirtiedFunc(dirtiedFunc); +} + +YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeConstRef node) { + return resolveRef(node)->getDirtiedFunc(); +} + +void YGNodeInsertChild( + const YGNodeRef ownerRef, + const YGNodeRef childRef, + const size_t index) { + auto owner = resolveRef(ownerRef); + auto child = resolveRef(childRef); + + yoga::assertFatalWithNode( + owner, + child->getOwner() == nullptr, + "Child already has a owner, it must be removed first."); + + yoga::assertFatalWithNode( + owner, + !owner->hasMeasureFunc(), + "Cannot add child: Nodes with measure functions cannot have children."); + + owner->insertChild(child, index); + child->setOwner(owner); + owner->markDirtyAndPropagate(); +} + +void YGNodeSwapChild( + const YGNodeRef ownerRef, + const YGNodeRef childRef, + const size_t index) { + auto owner = resolveRef(ownerRef); + auto child = resolveRef(childRef); + + owner->replaceChild(child, index); + child->setOwner(owner); +} + +void YGNodeRemoveChild( + const YGNodeRef ownerRef, + const YGNodeRef excludedChildRef) { + auto owner = resolveRef(ownerRef); + auto excludedChild = resolveRef(excludedChildRef); + + if (owner->getChildCount() == 0) { + // This is an empty set. Nothing to remove. + return; + } + + // Children may be shared between parents, which is indicated by not having an + // owner. We only want to reset the child completely if it is owned + // exclusively by one node. + auto childOwner = excludedChild->getOwner(); + if (owner->removeChild(excludedChild)) { + if (owner == childOwner) { + excludedChild->setLayout({}); // layout is no longer valid + excludedChild->setOwner(nullptr); + } + owner->markDirtyAndPropagate(); + } +} + +void YGNodeRemoveAllChildren(const YGNodeRef ownerRef) { + auto owner = resolveRef(ownerRef); + + const size_t childCount = owner->getChildCount(); + if (childCount == 0) { + // This is an empty set already. Nothing to do. + return; + } + auto* firstChild = owner->getChild(0); + if (firstChild->getOwner() == owner) { + // If the first child has this node as its owner, we assume that this child + // set is unique. + for (size_t i = 0; i < childCount; i++) { + yoga::Node* oldChild = owner->getChild(i); + oldChild->setLayout({}); // layout is no longer valid + oldChild->setOwner(nullptr); + } + owner->clearChildren(); + owner->markDirtyAndPropagate(); + return; + } + // Otherwise, we are not the owner of the child set. We don't have to do + // anything to clear it. + owner->setChildren({}); + owner->markDirtyAndPropagate(); +} + +void YGNodeSetChildren( + const YGNodeRef ownerRef, + const YGNodeRef* childrenRefs, + const size_t count) { + auto owner = resolveRef(ownerRef); + auto children = reinterpret_cast(childrenRefs); + + if (owner == nullptr) { + return; + } + + const std::vector childrenVector = {children, children + count}; + if (childrenVector.empty()) { + if (owner->getChildCount() > 0) { + for (auto* child : owner->getChildren()) { + child->setLayout({}); + child->setOwner(nullptr); + } + owner->setChildren({}); + owner->markDirtyAndPropagate(); + } + } else { + if (owner->getChildCount() > 0) { + for (auto* oldChild : owner->getChildren()) { + // Our new children may have nodes in common with the old children. We + // don't reset these common nodes. + if (std::find(childrenVector.begin(), childrenVector.end(), oldChild) == + childrenVector.end()) { + oldChild->setLayout({}); + oldChild->setOwner(nullptr); + } + } + } + owner->setChildren(childrenVector); + for (yoga::Node* child : childrenVector) { + child->setOwner(owner); + } + owner->markDirtyAndPropagate(); + } +} + +YGNodeRef YGNodeGetChild(const YGNodeRef nodeRef, const size_t index) { + const auto node = resolveRef(nodeRef); + + if (index < node->getChildren().size()) { + return node->getChild(index); + } + return nullptr; +} + +size_t YGNodeGetChildCount(const YGNodeConstRef node) { + return resolveRef(node)->getChildren().size(); +} + +YGNodeRef YGNodeGetOwner(const YGNodeRef node) { + return resolveRef(node)->getOwner(); +} + +YGNodeRef YGNodeGetParent(const YGNodeRef node) { + return resolveRef(node)->getOwner(); +} + +void YGNodeSetConfig(YGNodeRef node, YGConfigRef config) { + resolveRef(node)->setConfig(resolveRef(config)); +} + +YGConfigConstRef YGNodeGetConfig(YGNodeRef node) { + return resolveRef(node)->getConfig(); +} + +void YGNodeSetContext(YGNodeRef node, void* context) { + return resolveRef(node)->setContext(context); +} + +void* YGNodeGetContext(YGNodeConstRef node) { + return resolveRef(node)->getContext(); +} + +void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) { + resolveRef(node)->setMeasureFunc(measureFunc); +} + +bool YGNodeHasMeasureFunc(YGNodeConstRef node) { + return resolveRef(node)->hasMeasureFunc(); +} + +void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc) { + resolveRef(node)->setBaselineFunc(baselineFunc); +} + +bool YGNodeHasBaselineFunc(YGNodeConstRef node) { + return resolveRef(node)->hasBaselineFunc(); +} + +void YGNodeSetIsReferenceBaseline(YGNodeRef nodeRef, bool isReferenceBaseline) { + const auto node = resolveRef(nodeRef); + if (node->isReferenceBaseline() != isReferenceBaseline) { + node->setIsReferenceBaseline(isReferenceBaseline); + node->markDirtyAndPropagate(); + } +} + +bool YGNodeIsReferenceBaseline(YGNodeConstRef node) { + return resolveRef(node)->isReferenceBaseline(); +} + +void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType) { + return resolveRef(node)->setNodeType(scopedEnum(nodeType)); +} + +YGNodeType YGNodeGetNodeType(YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->getNodeType()); +} + +void YGNodeSetAlwaysFormsContainingBlock( + YGNodeRef node, + bool alwaysFormsContainingBlock) { + resolveRef(node)->setAlwaysFormsContainingBlock(alwaysFormsContainingBlock); +} + +bool YGNodeGetAlwaysFormsContainingBlock(YGNodeConstRef node) { + return resolveRef(node)->alwaysFormsContainingBlock(); +} + +// TODO: This leaks internal details to the public API. Remove after removing +// ComponentKit usage of it. +bool YGNodeCanUseCachedMeasurement( + YGMeasureMode widthMode, + float availableWidth, + YGMeasureMode heightMode, + float availableHeight, + YGMeasureMode lastWidthMode, + float lastAvailableWidth, + YGMeasureMode lastHeightMode, + float lastAvailableHeight, + float lastComputedWidth, + float lastComputedHeight, + float marginRow, + float marginColumn, + YGConfigRef config) { + return yoga::canUseCachedMeasurement( + sizingMode(scopedEnum(widthMode)), + availableWidth, + sizingMode(scopedEnum(heightMode)), + availableHeight, + sizingMode(scopedEnum(lastWidthMode)), + lastAvailableWidth, + sizingMode(scopedEnum(lastHeightMode)), + lastAvailableHeight, + lastComputedWidth, + lastComputedHeight, + marginRow, + marginColumn, + resolveRef(config)); +} diff --git a/Source/3rdParty/yoga/YGNode.h b/Source/3rdParty/yoga/YGNode.h new file mode 100644 index 000000000..8c34d09aa --- /dev/null +++ b/Source/3rdParty/yoga/YGNode.h @@ -0,0 +1,304 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include "yoga/YGConfig.h" +#include "yoga/YGEnums.h" +#include "yoga/YGMacros.h" + +YG_EXTERN_C_BEGIN + +/** + * Handle to a mutable Yoga Node. + */ +typedef struct YGNode* YGNodeRef; + +/** + * Handle to an immutable Yoga Node. + */ +typedef const struct YGNode* YGNodeConstRef; + +/** + * Heap allocates and returns a new Yoga node using Yoga settings. + */ +YG_EXPORT YGNodeRef YGNodeNew(void); + +/** + * Heap allocates and returns a new Yoga node, with customized settings. + */ +YG_EXPORT YGNodeRef YGNodeNewWithConfig(YGConfigConstRef config); + +/** + * Returns a mutable copy of an existing node, with the same context and + * children, but no owner set. Does not call the function set by + * YGConfigSetCloneNodeFunc(). + */ +YG_EXPORT YGNodeRef YGNodeClone(YGNodeConstRef node); + +/** + * Frees the Yoga node, disconnecting it from its owner and children. + */ +YG_EXPORT void YGNodeFree(YGNodeRef node); + +/** + * Frees the subtree of Yoga nodes rooted at the given node. + */ +YG_EXPORT void YGNodeFreeRecursive(YGNodeRef node); + +/** + * Frees the Yoga node without disconnecting it from its owner or children. + * Allows garbage collecting Yoga nodes in parallel when the entire tree is + * unreachable. + */ +YG_EXPORT void YGNodeFinalize(YGNodeRef node); + +/** + * Resets the node to its default state. + */ +YG_EXPORT void YGNodeReset(YGNodeRef node); + +/** + * Calculates the layout of the tree rooted at the given node. + * + * Layout results may be read after calling YGNodeCalculateLayout() using + * functions like YGNodeLayoutGetLeft(), YGNodeLayoutGetTop(), etc. + * + * YGNodeGetHasNewLayout() may be read to know if the layout of the node or its + * subtrees may have changed since the last time YGNodeCalculate() was called. + */ +YG_EXPORT void YGNodeCalculateLayout( + YGNodeRef node, + float availableWidth, + float availableHeight, + YGDirection ownerDirection); + +/** + * Whether the given node may have new layout results. Must be reset by calling + * YGNodeSetHasNewLayout(). + */ +YG_EXPORT bool YGNodeGetHasNewLayout(YGNodeConstRef node); + +/** + * Sets whether a nodes layout is considered new. + */ +YG_EXPORT void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout); + +/** + * Whether the node's layout results are dirty due to it or its children + * changing. + */ +YG_EXPORT bool YGNodeIsDirty(YGNodeConstRef node); + +/** + * Marks a node with custom measure function as dirty. + */ +YG_EXPORT void YGNodeMarkDirty(YGNodeRef node); + +typedef void (*YGDirtiedFunc)(YGNodeConstRef node); + +/** + * Called when a change is made to the Yoga tree which dirties this node. + */ +YG_EXPORT void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc); + +/** + * Returns a dirtied func if set. + */ +YG_EXPORT YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeConstRef node); + +/** + * Inserts a child node at the given index. + */ +YG_EXPORT void YGNodeInsertChild(YGNodeRef node, YGNodeRef child, size_t index); + +/** + * Replaces the child node at a given index with a new one. + */ +YG_EXPORT void YGNodeSwapChild(YGNodeRef node, YGNodeRef child, size_t index); + +/** + * Removes the given child node. + */ +YG_EXPORT void YGNodeRemoveChild(YGNodeRef node, YGNodeRef child); + +/** + * Removes all children nodes. + */ +YG_EXPORT void YGNodeRemoveAllChildren(YGNodeRef node); + +/** + * Sets children according to the given list of nodes. + */ +YG_EXPORT void +YGNodeSetChildren(YGNodeRef owner, const YGNodeRef* children, size_t count); + +/** + * Get the child node at a given index. + */ +YG_EXPORT YGNodeRef YGNodeGetChild(YGNodeRef node, size_t index); + +/** + * The number of child nodes. + */ +YG_EXPORT size_t YGNodeGetChildCount(YGNodeConstRef node); + +/** + * Get the parent/owner currently set for a node. + */ +YG_EXPORT YGNodeRef YGNodeGetOwner(YGNodeRef node); + +/** + * Get the parent/owner currently set for a node. + */ +YG_EXPORT YGNodeRef YGNodeGetParent(YGNodeRef node); + +/** + * Set a new config for the node after creation. + */ +YG_EXPORT void YGNodeSetConfig(YGNodeRef node, YGConfigRef config); + +/** + * Get the config currently set on the node. + */ +YG_EXPORT YGConfigConstRef YGNodeGetConfig(YGNodeRef node); + +/** + * Sets extra data on the Yoga node which may be read from during callbacks. + */ +YG_EXPORT void YGNodeSetContext(YGNodeRef node, void* context); + +/** + * Returns the context or NULL if no context has been set. + */ +YG_EXPORT void* YGNodeGetContext(YGNodeConstRef node); + +typedef struct YGSize { + float width; + float height; +} YGSize; + +/** + * Returns the computed dimensions of the node, following the constraints of + * `widthMode` and `heightMode`: + * + * YGMeasureModeUndefined: The parent has not imposed any constraint on the + * child. It can be whatever size it wants. + * + * YGMeasureModeAtMost: The child can be as large as it wants up to the + * specified size. + * + * YGMeasureModeExactly: The parent has determined an exact size for the + * child. The child is going to be given those bounds regardless of how big it + * wants to be. + * + * @returns the size of the leaf node, measured under the given constraints. + */ +typedef YGSize (*YGMeasureFunc)( + YGNodeConstRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode); + +/** + * Allows providing custom measurements for a Yoga leaf node (usually for + * measuring text). YGNodeMarkDirty() must be set if content effecting the + * measurements of the node changes. + */ +YG_EXPORT void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc); + +/** + * Whether a measure function is set. + */ +YG_EXPORT bool YGNodeHasMeasureFunc(YGNodeConstRef node); + +/** + * @returns a defined offset to baseline (ascent). + */ +typedef float (*YGBaselineFunc)(YGNodeConstRef node, float width, float height); + +/** + * Set a custom function for determining the text baseline for use in baseline + * alignment. + */ +YG_EXPORT void YGNodeSetBaselineFunc( + YGNodeRef node, + YGBaselineFunc baselineFunc); + +/** + * Whether a baseline function is set. + */ +YG_EXPORT bool YGNodeHasBaselineFunc(YGNodeConstRef node); + +/** + * Sets this node should be considered the reference baseline among siblings. + */ +YG_EXPORT void YGNodeSetIsReferenceBaseline( + YGNodeRef node, + bool isReferenceBaseline); + +/** + * Whether this node is set as the reference baseline. + */ +YG_EXPORT bool YGNodeIsReferenceBaseline(YGNodeConstRef node); + +/** + * Sets whether a leaf node's layout results may be truncated during layout + * rounding. + */ +YG_EXPORT void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType); + +/** + * Wwhether a leaf node's layout results may be truncated during layout + * rounding. + */ +YG_EXPORT YGNodeType YGNodeGetNodeType(YGNodeConstRef node); + +/** + * Make it so that this node will always form a containing block for any + * descendant nodes. This is useful for when a node has a property outside of + * of Yoga that will form a containing block. For example, transforms or some of + * the others listed in + * https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block + */ +YG_EXPORT void YGNodeSetAlwaysFormsContainingBlock( + YGNodeRef node, + bool alwaysFormsContainingBlock); + +/** + * Whether the node will always form a containing block for any descendant. This + * can happen in situation where the client implements something like a + * transform that can affect containing blocks but is not handled by Yoga + * directly. + */ +YG_EXPORT bool YGNodeGetAlwaysFormsContainingBlock(YGNodeConstRef node); + +/** + * @deprecated + */ +YG_DEPRECATED( + "YGNodeCanUseCachedMeasurement may be removed in a future version of Yoga") +YG_EXPORT bool YGNodeCanUseCachedMeasurement( + YGMeasureMode widthMode, + float availableWidth, + YGMeasureMode heightMode, + float availableHeight, + YGMeasureMode lastWidthMode, + float lastAvailableWidth, + YGMeasureMode lastHeightMode, + float lastAvailableHeight, + float lastComputedWidth, + float lastComputedHeight, + float marginRow, + float marginColumn, + YGConfigRef config); + +YG_EXTERN_C_END diff --git a/Source/3rdParty/yoga/YGNodeLayout.cpp b/Source/3rdParty/yoga/YGNodeLayout.cpp new file mode 100644 index 000000000..c2087c013 --- /dev/null +++ b/Source/3rdParty/yoga/YGNodeLayout.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/Yoga.h" +#include "yoga/debug/AssertFatal.h" +#include "yoga/enums/Edge.h" +#include "yoga/node/Node.h" + +using namespace facebook; +using namespace facebook::yoga; + +namespace { + +template +float getResolvedLayoutProperty(const YGNodeConstRef nodeRef, const Edge edge) { + const auto node = resolveRef(nodeRef); + yoga::assertFatalWithNode( + node, + edge <= Edge::End, + "Cannot get layout properties of multi-edge shorthands"); + + if (edge == Edge::Start) { + if (node->getLayout().direction() == Direction::RTL) { + return (node->getLayout().*LayoutMember)(PhysicalEdge::Right); + } else { + return (node->getLayout().*LayoutMember)(PhysicalEdge::Left); + } + } + + if (edge == Edge::End) { + if (node->getLayout().direction() == Direction::RTL) { + return (node->getLayout().*LayoutMember)(PhysicalEdge::Left); + } else { + return (node->getLayout().*LayoutMember)(PhysicalEdge::Right); + } + } + + return (node->getLayout().*LayoutMember)(static_cast(edge)); +} + +} // namespace + +float YGNodeLayoutGetLeft(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().position(PhysicalEdge::Left); +} + +float YGNodeLayoutGetTop(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().position(PhysicalEdge::Top); +} + +float YGNodeLayoutGetRight(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().position(PhysicalEdge::Right); +} + +float YGNodeLayoutGetBottom(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().position(PhysicalEdge::Bottom); +} + +float YGNodeLayoutGetWidth(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().dimension(Dimension::Width); +} + +float YGNodeLayoutGetHeight(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().dimension(Dimension::Height); +} + +YGDirection YGNodeLayoutGetDirection(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->getLayout().direction()); +} + +bool YGNodeLayoutGetHadOverflow(const YGNodeConstRef node) { + return resolveRef(node)->getLayout().hadOverflow(); +} + +float YGNodeLayoutGetMargin(YGNodeConstRef node, YGEdge edge) { + return getResolvedLayoutProperty<&LayoutResults::margin>( + node, scopedEnum(edge)); +} + +float YGNodeLayoutGetBorder(YGNodeConstRef node, YGEdge edge) { + return getResolvedLayoutProperty<&LayoutResults::border>( + node, scopedEnum(edge)); +} + +float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge) { + return getResolvedLayoutProperty<&LayoutResults::padding>( + node, scopedEnum(edge)); +} diff --git a/Source/3rdParty/yoga/YGNodeLayout.h b/Source/3rdParty/yoga/YGNodeLayout.h new file mode 100644 index 000000000..d13895969 --- /dev/null +++ b/Source/3rdParty/yoga/YGNodeLayout.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "yoga/YGConfig.h" +#include "yoga/YGEnums.h" +#include "yoga/YGMacros.h" + +YG_EXTERN_C_BEGIN + +YG_EXPORT float YGNodeLayoutGetLeft(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetTop(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetRight(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetBottom(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetWidth(YGNodeConstRef node); +YG_EXPORT float YGNodeLayoutGetHeight(YGNodeConstRef node); +YG_EXPORT YGDirection YGNodeLayoutGetDirection(YGNodeConstRef node); +YG_EXPORT bool YGNodeLayoutGetHadOverflow(YGNodeConstRef node); + +// Get the computed values for these nodes after performing layout. If they were +// set using point values then the returned value will be the same as +// YGNodeStyleGetXXX. However if they were set using a percentage value then the +// returned value is the computed value used during layout. +YG_EXPORT float YGNodeLayoutGetMargin(YGNodeConstRef node, YGEdge edge); +YG_EXPORT float YGNodeLayoutGetBorder(YGNodeConstRef node, YGEdge edge); +YG_EXPORT float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge); + +YG_EXTERN_C_END diff --git a/Source/3rdParty/yoga/YGNodeStyle.cpp b/Source/3rdParty/yoga/YGNodeStyle.cpp new file mode 100644 index 000000000..b596400d7 --- /dev/null +++ b/Source/3rdParty/yoga/YGNodeStyle.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/Yoga.h" +#include "yoga/debug/AssertFatal.h" +#include "yoga/node/Node.h" + +using namespace facebook; +using namespace facebook::yoga; + +namespace { + +template +void updateStyle(YGNodeRef node, ValueT value) { + auto& style = resolveRef(node)->style(); + if ((style.*GetterT)() != value) { + (style.*SetterT)(value); + resolveRef(node)->markDirtyAndPropagate(); + } +} + +template +void updateStyle(YGNodeRef node, IdxT idx, ValueT value) { + auto& style = resolveRef(node)->style(); + if ((style.*GetterT)(idx) != value) { + (style.*SetterT)(idx, value); + resolveRef(node)->markDirtyAndPropagate(); + } +} + +} // namespace + +void YGNodeCopyStyle(YGNodeRef dstNode, YGNodeConstRef srcNode) { + auto dst = resolveRef(dstNode); + auto src = resolveRef(srcNode); + + if (dst->style() != src->style()) { + dst->setStyle(src->style()); + dst->markDirtyAndPropagate(); + } +} + +void YGNodeStyleSetDirection(const YGNodeRef node, const YGDirection value) { + updateStyle<&Style::direction, &Style::setDirection>(node, scopedEnum(value)); +} + +YGDirection YGNodeStyleGetDirection(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().direction()); +} + +void YGNodeStyleSetFlexDirection( + const YGNodeRef node, + const YGFlexDirection flexDirection) { + updateStyle<&Style::flexDirection, &Style::setFlexDirection>( + node, scopedEnum(flexDirection)); +} + +YGFlexDirection YGNodeStyleGetFlexDirection(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().flexDirection()); +} + +void YGNodeStyleSetJustifyContent( + const YGNodeRef node, + const YGJustify justifyContent) { + updateStyle<&Style::justifyContent, &Style::setJustifyContent>( + node, scopedEnum(justifyContent)); +} + +YGJustify YGNodeStyleGetJustifyContent(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().justifyContent()); +} + +void YGNodeStyleSetAlignContent( + const YGNodeRef node, + const YGAlign alignContent) { + updateStyle<&Style::alignContent, &Style::setAlignContent>( + node, scopedEnum(alignContent)); +} + +YGAlign YGNodeStyleGetAlignContent(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().alignContent()); +} + +void YGNodeStyleSetAlignItems(const YGNodeRef node, const YGAlign alignItems) { + updateStyle<&Style::alignItems, &Style::setAlignItems>( + node, scopedEnum(alignItems)); +} + +YGAlign YGNodeStyleGetAlignItems(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().alignItems()); +} + +void YGNodeStyleSetAlignSelf(const YGNodeRef node, const YGAlign alignSelf) { + updateStyle<&Style::alignSelf, &Style::setAlignSelf>( + node, scopedEnum(alignSelf)); +} + +YGAlign YGNodeStyleGetAlignSelf(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().alignSelf()); +} + +void YGNodeStyleSetPositionType( + const YGNodeRef node, + const YGPositionType positionType) { + updateStyle<&Style::positionType, &Style::setPositionType>( + node, scopedEnum(positionType)); +} + +YGPositionType YGNodeStyleGetPositionType(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().positionType()); +} + +void YGNodeStyleSetFlexWrap(const YGNodeRef node, const YGWrap flexWrap) { + updateStyle<&Style::flexWrap, &Style::setFlexWrap>( + node, scopedEnum(flexWrap)); +} + +YGWrap YGNodeStyleGetFlexWrap(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().flexWrap()); +} + +void YGNodeStyleSetOverflow(const YGNodeRef node, const YGOverflow overflow) { + updateStyle<&Style::overflow, &Style::setOverflow>( + node, scopedEnum(overflow)); +} + +YGOverflow YGNodeStyleGetOverflow(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().overflow()); +} + +void YGNodeStyleSetDisplay(const YGNodeRef node, const YGDisplay display) { + updateStyle<&Style::display, &Style::setDisplay>(node, scopedEnum(display)); +} + +YGDisplay YGNodeStyleGetDisplay(const YGNodeConstRef node) { + return unscopedEnum(resolveRef(node)->style().display()); +} + +void YGNodeStyleSetFlex(const YGNodeRef node, const float flex) { + updateStyle<&Style::flex, &Style::setFlex>(node, FloatOptional{flex}); +} + +float YGNodeStyleGetFlex(const YGNodeConstRef nodeRef) { + const auto node = resolveRef(nodeRef); + return node->style().flex().isUndefined() ? YGUndefined + : node->style().flex().unwrap(); +} + +void YGNodeStyleSetFlexGrow(const YGNodeRef node, const float flexGrow) { + updateStyle<&Style::flexGrow, &Style::setFlexGrow>( + node, FloatOptional{flexGrow}); +} + +float YGNodeStyleGetFlexGrow(const YGNodeConstRef nodeRef) { + const auto node = resolveRef(nodeRef); + return node->style().flexGrow().isUndefined() + ? Style::DefaultFlexGrow + : node->style().flexGrow().unwrap(); +} + +void YGNodeStyleSetFlexShrink(const YGNodeRef node, const float flexShrink) { + updateStyle<&Style::flexShrink, &Style::setFlexShrink>( + node, FloatOptional{flexShrink}); +} + +float YGNodeStyleGetFlexShrink(const YGNodeConstRef nodeRef) { + const auto node = resolveRef(nodeRef); + return node->style().flexShrink().isUndefined() + ? (node->getConfig()->useWebDefaults() ? Style::WebDefaultFlexShrink + : Style::DefaultFlexShrink) + : node->style().flexShrink().unwrap(); +} + +void YGNodeStyleSetFlexBasis(const YGNodeRef node, const float flexBasis) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, value::points(flexBasis)); +} + +void YGNodeStyleSetFlexBasisPercent( + const YGNodeRef node, + const float flexBasisPercent) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, value::percent(flexBasisPercent)); +} + +void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>(node, value::ofAuto()); +} + +YGValue YGNodeStyleGetFlexBasis(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().flexBasis(); +} + +void YGNodeStyleSetPosition(YGNodeRef node, YGEdge edge, float points) { + updateStyle<&Style::position, &Style::setPosition>( + node, scopedEnum(edge), value::points(points)); +} + +void YGNodeStyleSetPositionPercent(YGNodeRef node, YGEdge edge, float percent) { + updateStyle<&Style::position, &Style::setPosition>( + node, scopedEnum(edge), value::percent(percent)); +} + +YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge) { + return (YGValue)resolveRef(node)->style().position(scopedEnum(edge)); +} + +void YGNodeStyleSetMargin(YGNodeRef node, YGEdge edge, float points) { + updateStyle<&Style::margin, &Style::setMargin>( + node, scopedEnum(edge), value::points(points)); +} + +void YGNodeStyleSetMarginPercent(YGNodeRef node, YGEdge edge, float percent) { + updateStyle<&Style::margin, &Style::setMargin>( + node, scopedEnum(edge), value::percent(percent)); +} + +void YGNodeStyleSetMarginAuto(YGNodeRef node, YGEdge edge) { + updateStyle<&Style::margin, &Style::setMargin>( + node, scopedEnum(edge), value::ofAuto()); +} + +YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge) { + return (YGValue)resolveRef(node)->style().margin(scopedEnum(edge)); +} + +void YGNodeStyleSetPadding(YGNodeRef node, YGEdge edge, float points) { + updateStyle<&Style::padding, &Style::setPadding>( + node, scopedEnum(edge), value::points(points)); +} + +void YGNodeStyleSetPaddingPercent(YGNodeRef node, YGEdge edge, float percent) { + updateStyle<&Style::padding, &Style::setPadding>( + node, scopedEnum(edge), value::percent(percent)); +} + +YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge) { + return (YGValue)resolveRef(node)->style().padding(scopedEnum(edge)); +} + +void YGNodeStyleSetBorder( + const YGNodeRef node, + const YGEdge edge, + const float border) { + updateStyle<&Style::border, &Style::setBorder>( + node, scopedEnum(edge), value::points(border)); +} + +float YGNodeStyleGetBorder(const YGNodeConstRef node, const YGEdge edge) { + auto border = resolveRef(node)->style().border(scopedEnum(edge)); + if (border.isUndefined() || border.isAuto()) { + return YGUndefined; + } + + return static_cast(border).value; +} + +void YGNodeStyleSetGap( + const YGNodeRef node, + const YGGutter gutter, + const float gapLength) { + updateStyle<&Style::gap, &Style::setGap>( + node, scopedEnum(gutter), value::points(gapLength)); +} + +void YGNodeStyleSetGapPercent(YGNodeRef node, YGGutter gutter, float percent) { + updateStyle<&Style::gap, &Style::setGap>( + node, scopedEnum(gutter), value::percent(percent)); +} + +float YGNodeStyleGetGap(const YGNodeConstRef node, const YGGutter gutter) { + auto gapLength = resolveRef(node)->style().gap(scopedEnum(gutter)); + if (gapLength.isUndefined() || gapLength.isAuto()) { + return YGUndefined; + } + + return static_cast(gapLength).value; +} + +void YGNodeStyleSetAspectRatio(const YGNodeRef node, const float aspectRatio) { + updateStyle<&Style::aspectRatio, &Style::setAspectRatio>( + node, FloatOptional{aspectRatio}); +} + +float YGNodeStyleGetAspectRatio(const YGNodeConstRef node) { + const FloatOptional op = resolveRef(node)->style().aspectRatio(); + return op.isUndefined() ? YGUndefined : op.unwrap(); +} + +void YGNodeStyleSetWidth(YGNodeRef node, float points) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, value::points(points)); +} + +void YGNodeStyleSetWidthPercent(YGNodeRef node, float percent) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, value::percent(percent)); +} + +void YGNodeStyleSetWidthAuto(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, value::ofAuto()); +} + +YGValue YGNodeStyleGetWidth(YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().dimension(Dimension::Width); +} + +void YGNodeStyleSetHeight(YGNodeRef node, float points) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, value::points(points)); +} + +void YGNodeStyleSetHeightPercent(YGNodeRef node, float percent) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, value::percent(percent)); +} + +void YGNodeStyleSetHeightAuto(YGNodeRef node) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, value::ofAuto()); +} + +YGValue YGNodeStyleGetHeight(YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().dimension(Dimension::Height); +} + +void YGNodeStyleSetMinWidth(const YGNodeRef node, const float minWidth) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Width, value::points(minWidth)); +} + +void YGNodeStyleSetMinWidthPercent(const YGNodeRef node, const float minWidth) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Width, value::percent(minWidth)); +} + +YGValue YGNodeStyleGetMinWidth(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().minDimension(Dimension::Width); +} + +void YGNodeStyleSetMinHeight(const YGNodeRef node, const float minHeight) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Height, value::points(minHeight)); +} + +void YGNodeStyleSetMinHeightPercent( + const YGNodeRef node, + const float minHeight) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Height, value::percent(minHeight)); +} + +YGValue YGNodeStyleGetMinHeight(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().minDimension(Dimension::Height); +} + +void YGNodeStyleSetMaxWidth(const YGNodeRef node, const float maxWidth) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Width, value::points(maxWidth)); +} + +void YGNodeStyleSetMaxWidthPercent(const YGNodeRef node, const float maxWidth) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Width, value::percent(maxWidth)); +} + +YGValue YGNodeStyleGetMaxWidth(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().maxDimension(Dimension::Width); +} + +void YGNodeStyleSetMaxHeight(const YGNodeRef node, const float maxHeight) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Height, value::points(maxHeight)); +} + +void YGNodeStyleSetMaxHeightPercent( + const YGNodeRef node, + const float maxHeight) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Height, value::percent(maxHeight)); +} + +YGValue YGNodeStyleGetMaxHeight(const YGNodeConstRef node) { + return (YGValue)resolveRef(node)->style().maxDimension(Dimension::Height); +} diff --git a/Source/3rdParty/yoga/YGNodeStyle.h b/Source/3rdParty/yoga/YGNodeStyle.h new file mode 100644 index 000000000..26c1403c7 --- /dev/null +++ b/Source/3rdParty/yoga/YGNodeStyle.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "yoga/YGNode.h" +#include "yoga/YGValue.h" + +YG_EXTERN_C_BEGIN + +YG_EXPORT void YGNodeCopyStyle(YGNodeRef dstNode, YGNodeConstRef srcNode); + +YG_EXPORT void YGNodeStyleSetDirection(YGNodeRef node, YGDirection direction); +YG_EXPORT YGDirection YGNodeStyleGetDirection(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexDirection( + YGNodeRef node, + YGFlexDirection flexDirection); +YG_EXPORT YGFlexDirection YGNodeStyleGetFlexDirection(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetJustifyContent( + YGNodeRef node, + YGJustify justifyContent); +YG_EXPORT YGJustify YGNodeStyleGetJustifyContent(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetAlignContent(YGNodeRef node, YGAlign alignContent); +YG_EXPORT YGAlign YGNodeStyleGetAlignContent(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetAlignItems(YGNodeRef node, YGAlign alignItems); +YG_EXPORT YGAlign YGNodeStyleGetAlignItems(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetAlignSelf(YGNodeRef node, YGAlign alignSelf); +YG_EXPORT YGAlign YGNodeStyleGetAlignSelf(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetPositionType( + YGNodeRef node, + YGPositionType positionType); +YG_EXPORT YGPositionType YGNodeStyleGetPositionType(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexWrap(YGNodeRef node, YGWrap flexWrap); +YG_EXPORT YGWrap YGNodeStyleGetFlexWrap(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetOverflow(YGNodeRef node, YGOverflow overflow); +YG_EXPORT YGOverflow YGNodeStyleGetOverflow(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetDisplay(YGNodeRef node, YGDisplay display); +YG_EXPORT YGDisplay YGNodeStyleGetDisplay(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlex(YGNodeRef node, float flex); +YG_EXPORT float YGNodeStyleGetFlex(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexGrow(YGNodeRef node, float flexGrow); +YG_EXPORT float YGNodeStyleGetFlexGrow(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexShrink(YGNodeRef node, float flexShrink); +YG_EXPORT float YGNodeStyleGetFlexShrink(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetFlexBasis(YGNodeRef node, float flexBasis); +YG_EXPORT void YGNodeStyleSetFlexBasisPercent(YGNodeRef node, float flexBasis); +YG_EXPORT void YGNodeStyleSetFlexBasisAuto(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetFlexBasis(YGNodeConstRef node); + +YG_EXPORT void +YGNodeStyleSetPosition(YGNodeRef node, YGEdge edge, float position); +YG_EXPORT void +YGNodeStyleSetPositionPercent(YGNodeRef node, YGEdge edge, float position); +YG_EXPORT YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge); + +YG_EXPORT void YGNodeStyleSetMargin(YGNodeRef node, YGEdge edge, float margin); +YG_EXPORT void +YGNodeStyleSetMarginPercent(YGNodeRef node, YGEdge edge, float margin); +YG_EXPORT void YGNodeStyleSetMarginAuto(YGNodeRef node, YGEdge edge); +YG_EXPORT YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge); + +YG_EXPORT void +YGNodeStyleSetPadding(YGNodeRef node, YGEdge edge, float padding); +YG_EXPORT void +YGNodeStyleSetPaddingPercent(YGNodeRef node, YGEdge edge, float padding); +YG_EXPORT YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge); + +YG_EXPORT void YGNodeStyleSetBorder(YGNodeRef node, YGEdge edge, float border); +YG_EXPORT float YGNodeStyleGetBorder(YGNodeConstRef node, YGEdge edge); + +YG_EXPORT void +YGNodeStyleSetGap(YGNodeRef node, YGGutter gutter, float gapLength); +YG_EXPORT void +YGNodeStyleSetGapPercent(YGNodeRef node, YGGutter gutter, float gapLength); +YG_EXPORT float YGNodeStyleGetGap(YGNodeConstRef node, YGGutter gutter); + +YG_EXPORT void YGNodeStyleSetWidth(YGNodeRef node, float width); +YG_EXPORT void YGNodeStyleSetWidthPercent(YGNodeRef node, float width); +YG_EXPORT void YGNodeStyleSetWidthAuto(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetWidth(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetHeight(YGNodeRef node, float height); +YG_EXPORT void YGNodeStyleSetHeightPercent(YGNodeRef node, float height); +YG_EXPORT void YGNodeStyleSetHeightAuto(YGNodeRef node); +YG_EXPORT YGValue YGNodeStyleGetHeight(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetMinWidth(YGNodeRef node, float minWidth); +YG_EXPORT void YGNodeStyleSetMinWidthPercent(YGNodeRef node, float minWidth); +YG_EXPORT YGValue YGNodeStyleGetMinWidth(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetMinHeight(YGNodeRef node, float minHeight); +YG_EXPORT void YGNodeStyleSetMinHeightPercent(YGNodeRef node, float minHeight); +YG_EXPORT YGValue YGNodeStyleGetMinHeight(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetMaxWidth(YGNodeRef node, float maxWidth); +YG_EXPORT void YGNodeStyleSetMaxWidthPercent(YGNodeRef node, float maxWidth); +YG_EXPORT YGValue YGNodeStyleGetMaxWidth(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetMaxHeight(YGNodeRef node, float maxHeight); +YG_EXPORT void YGNodeStyleSetMaxHeightPercent(YGNodeRef node, float maxHeight); +YG_EXPORT YGValue YGNodeStyleGetMaxHeight(YGNodeConstRef node); + +YG_EXPORT void YGNodeStyleSetAspectRatio(YGNodeRef node, float aspectRatio); +YG_EXPORT float YGNodeStyleGetAspectRatio(YGNodeConstRef node); + +YG_EXTERN_C_END diff --git a/Source/3rdParty/yoga/YGPixelGrid.cpp b/Source/3rdParty/yoga/YGPixelGrid.cpp new file mode 100644 index 000000000..70ab71d84 --- /dev/null +++ b/Source/3rdParty/yoga/YGPixelGrid.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/Yoga.h" + +#include "yoga/algorithm/PixelGrid.h" + +using namespace facebook; +using namespace facebook::yoga; + +float YGRoundValueToPixelGrid( + const double value, + const double pointScaleFactor, + const bool forceCeil, + const bool forceFloor) { + return yoga::roundValueToPixelGrid( + value, pointScaleFactor, forceCeil, forceFloor); +} diff --git a/Source/3rdParty/yoga/YGPixelGrid.h b/Source/3rdParty/yoga/YGPixelGrid.h new file mode 100644 index 000000000..16cb3cd77 --- /dev/null +++ b/Source/3rdParty/yoga/YGPixelGrid.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "yoga/YGConfig.h" +#include "yoga/YGEnums.h" +#include "yoga/YGMacros.h" + +YG_EXTERN_C_BEGIN + +/** + * Rounds a point value to the nearest whole pixel, given a pointScaleFactor + * describing pixel density. + * @returns the rounded value in points + */ +YG_EXPORT float YGRoundValueToPixelGrid( + double value, + double pointScaleFactor, + bool forceCeil, + bool forceFloor); + +YG_EXTERN_C_END diff --git a/Source/3rdParty/yoga/YGValue.cpp b/Source/3rdParty/yoga/YGValue.cpp new file mode 100644 index 000000000..faf1b2c9d --- /dev/null +++ b/Source/3rdParty/yoga/YGValue.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/YGValue.h" +#include "yoga/numeric/Comparison.h" + +using namespace facebook; +using namespace facebook::yoga; + +const YGValue YGValueZero = {0, YGUnitPoint}; +const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined}; +const YGValue YGValueAuto = {YGUndefined, YGUnitAuto}; + +bool YGFloatIsUndefined(const float value) { + return yoga::isUndefined(value); +} diff --git a/Source/3rdParty/yoga/YGValue.h b/Source/3rdParty/yoga/YGValue.h new file mode 100644 index 000000000..77fcd3128 --- /dev/null +++ b/Source/3rdParty/yoga/YGValue.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "yoga/YGEnums.h" +#include "yoga/YGMacros.h" + +/** + * Float value to represent "undefined" in style values. + */ +#ifdef __cplusplus +#include +constexpr float YGUndefined = std::numeric_limits::quiet_NaN(); +#else +#include +#define YGUndefined NAN +#endif + +YG_EXTERN_C_BEGIN + +/** + * Structure used to represent a dimension in a style. + */ +typedef struct YGValue { + float value; + YGUnit unit; +} YGValue; + +/** + * Constant for a dimension of "auto". + */ +YG_EXPORT extern const YGValue YGValueAuto; + +/** + * Constant for a dimension which is not defined. + */ +YG_EXPORT extern const YGValue YGValueUndefined; + +/** + * Constant for a dimension that is zero-length. + */ +YG_EXPORT extern const YGValue YGValueZero; + +/** + * Whether a dimension represented as a float is defined. + */ +YG_EXPORT bool YGFloatIsUndefined(float value); + +YG_EXTERN_C_END + +// Equality operators for comparison of YGValue in C++ +#ifdef __cplusplus +inline bool operator==(const YGValue& lhs, const YGValue& rhs) { + if (lhs.unit != rhs.unit) { + return false; + } + + switch (lhs.unit) { + case YGUnitUndefined: + case YGUnitAuto: + return true; + case YGUnitPoint: + case YGUnitPercent: + return lhs.value == rhs.value; + } + + return false; +} + +inline bool operator!=(const YGValue& lhs, const YGValue& rhs) { + return !(lhs == rhs); +} + +inline YGValue operator-(const YGValue& value) { + return {-value.value, value.unit}; +} +#endif diff --git a/Source/3rdParty/yoga/Yoga.h b/Source/3rdParty/yoga/Yoga.h new file mode 100644 index 000000000..84abf2a03 --- /dev/null +++ b/Source/3rdParty/yoga/Yoga.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +/** + * `#include "yoga/Yoga.h"` includes all of Yoga's public headers. + */ + +#include "yoga/YGConfig.h" +#include "yoga/YGEnums.h" +#include "yoga/YGMacros.h" +#include "yoga/YGNode.h" +#include "yoga/YGNodeLayout.h" +#include "yoga/YGNodeStyle.h" +#include "yoga/YGPixelGrid.h" +#include "yoga/YGValue.h" diff --git a/Source/3rdParty/yoga/algorithm/AbsoluteLayout.cpp b/Source/3rdParty/yoga/algorithm/AbsoluteLayout.cpp new file mode 100644 index 000000000..6b3b6c7b6 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/AbsoluteLayout.cpp @@ -0,0 +1,598 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/algorithm/AbsoluteLayout.h" +#include "yoga/algorithm/Align.h" +#include "yoga/algorithm/BoundAxis.h" +#include "yoga/algorithm/CalculateLayout.h" +#include "yoga/algorithm/TrailingPosition.h" + +namespace facebook::yoga { + +static inline void setFlexStartLayoutPosition( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const float containingBlockWidth) { + child->setLayoutPosition( + child->style().computeFlexStartMargin( + axis, direction, containingBlockWidth) + + parent->getLayout().border(flexStartEdge(axis)) + + parent->getLayout().padding(flexStartEdge(axis)), + flexStartEdge(axis)); +} + +static inline void setFlexEndLayoutPosition( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const float containingBlockWidth) { + child->setLayoutPosition( + getPositionOfOppositeEdge( + parent->getLayout().border(flexEndEdge(axis)) + + parent->getLayout().padding(flexEndEdge(axis)) + + child->style().computeFlexEndMargin( + axis, direction, containingBlockWidth), + axis, + parent, + child), + flexStartEdge(axis)); +} + +static inline void setCenterLayoutPosition( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const float containingBlockWidth) { + const float parentContentBoxSize = + parent->getLayout().measuredDimension(dimension(axis)) - + parent->getLayout().border(flexStartEdge(axis)) - + parent->getLayout().border(flexEndEdge(axis)) - + parent->getLayout().padding(flexStartEdge(axis)) - + parent->getLayout().padding(flexEndEdge(axis)); + const float childOuterSize = + child->getLayout().measuredDimension(dimension(axis)) + + child->style().computeMarginForAxis(axis, containingBlockWidth); + child->setLayoutPosition( + (parentContentBoxSize - childOuterSize) / 2.0f + + parent->getLayout().border(flexStartEdge(axis)) + + parent->getLayout().padding(flexStartEdge(axis)) + + child->style().computeFlexStartMargin( + axis, direction, containingBlockWidth), + flexStartEdge(axis)); +} + +static void justifyAbsoluteChild( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection mainAxis, + const float containingBlockWidth) { + const Justify parentJustifyContent = parent->style().justifyContent(); + switch (parentJustifyContent) { + case Justify::FlexStart: + case Justify::SpaceBetween: + setFlexStartLayoutPosition( + parent, child, direction, mainAxis, containingBlockWidth); + break; + case Justify::FlexEnd: + setFlexEndLayoutPosition( + parent, child, direction, mainAxis, containingBlockWidth); + break; + case Justify::Center: + case Justify::SpaceAround: + case Justify::SpaceEvenly: + setCenterLayoutPosition( + parent, child, direction, mainAxis, containingBlockWidth); + break; + } +} + +static void alignAbsoluteChild( + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection crossAxis, + const float containingBlockWidth) { + Align itemAlign = resolveChildAlignment(parent, child); + const Wrap parentWrap = parent->style().flexWrap(); + if (parentWrap == Wrap::WrapReverse) { + if (itemAlign == Align::FlexEnd) { + itemAlign = Align::FlexStart; + } else if (itemAlign != Align::Center) { + itemAlign = Align::FlexEnd; + } + } + + switch (itemAlign) { + case Align::Auto: + case Align::FlexStart: + case Align::Baseline: + case Align::SpaceAround: + case Align::SpaceBetween: + case Align::Stretch: + case Align::SpaceEvenly: + setFlexStartLayoutPosition( + parent, child, direction, crossAxis, containingBlockWidth); + break; + case Align::FlexEnd: + setFlexEndLayoutPosition( + parent, child, direction, crossAxis, containingBlockWidth); + break; + case Align::Center: + setCenterLayoutPosition( + parent, child, direction, crossAxis, containingBlockWidth); + break; + } +} + +// To ensure no breaking changes, we preserve the legacy way of positioning +// absolute children and determine if we should use it using an errata. +static void positionAbsoluteChildLegacy( + const yoga::Node* const containingNode, + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const bool isMainAxis, + const float containingBlockWidth, + const float containingBlockHeight) { + const bool isAxisRow = isRow(axis); + const bool shouldCenter = isMainAxis + ? parent->style().justifyContent() == Justify::Center + : resolveChildAlignment(parent, child) == Align::Center; + const bool shouldFlexEnd = isMainAxis + ? parent->style().justifyContent() == Justify::FlexEnd + : ((resolveChildAlignment(parent, child) == Align::FlexEnd) ^ + (parent->style().flexWrap() == Wrap::WrapReverse)); + + if (child->style().isFlexEndPositionDefined(axis, direction) && + !child->style().isFlexStartPositionDefined(axis, direction)) { + child->setLayoutPosition( + containingNode->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis)) - + containingNode->style().computeFlexEndBorder(axis, direction) - + child->style().computeFlexEndMargin( + axis, + direction, + isAxisRow ? containingBlockWidth : containingBlockHeight) - + child->style().computeFlexEndPosition( + axis, + direction, + isAxisRow ? containingBlockWidth : containingBlockHeight), + flexStartEdge(axis)); + } else if ( + !child->style().isFlexStartPositionDefined(axis, direction) && + shouldCenter) { + child->setLayoutPosition( + (parent->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis))) / + 2.0f, + flexStartEdge(axis)); + } else if ( + !child->style().isFlexStartPositionDefined(axis, direction) && + shouldFlexEnd) { + child->setLayoutPosition( + (parent->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis))), + flexStartEdge(axis)); + } +} + +/* + * Absolutely positioned nodes do not participate in flex layout and thus their + * positions can be determined independently from the rest of their siblings. + * For each axis there are essentially two cases: + * + * 1) The node has insets defined. In this case we can just use these to + * determine the position of the node. + * 2) The node does not have insets defined. In this case we look at the style + * of the parent to position the node. Things like justify content and + * align content will move absolute children around. If none of these + * special properties are defined, the child is positioned at the start + * (defined by flex direction) of the leading flex line. + * + * This function does that positioning for the given axis. The spec has more + * information on this topic: https://www.w3.org/TR/css-flexbox-1/#abspos-items + */ +static void positionAbsoluteChildImpl( + const yoga::Node* const containingNode, + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const bool isMainAxis, + const float containingBlockWidth, + const float containingBlockHeight) { + const bool isAxisRow = isRow(axis); + const float containingBlockSize = + isAxisRow ? containingBlockWidth : containingBlockHeight; + + // The inline-start position takes priority over the end position in the case + // that they are both set and the node has a fixed width. Thus we only have 2 + // cases here: if inline-start is defined and if inline-end is defined. + // + // Despite checking inline-start to honor prioritization of insets, we write + // to the flex-start edge because this algorithm works by positioning on the + // flex-start edge and then filling in the flex-end direction at the end if + // necessary. + if (child->style().isInlineStartPositionDefined(axis, direction)) { + const float positionRelativeToInlineStart = + child->style().computeInlineStartPosition( + axis, direction, containingBlockSize) + + containingNode->style().computeInlineStartBorder(axis, direction) + + child->style().computeInlineStartMargin( + axis, direction, containingBlockSize); + const float positionRelativeToFlexStart = + inlineStartEdge(axis, direction) != flexStartEdge(axis) + ? getPositionOfOppositeEdge( + positionRelativeToInlineStart, axis, containingNode, child) + : positionRelativeToInlineStart; + + child->setLayoutPosition(positionRelativeToFlexStart, flexStartEdge(axis)); + } else if (child->style().isInlineEndPositionDefined(axis, direction)) { + const float positionRelativeToInlineStart = + containingNode->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis)) - + containingNode->style().computeInlineEndBorder(axis, direction) - + child->style().computeInlineEndMargin( + axis, direction, containingBlockSize) - + child->style().computeInlineEndPosition( + axis, direction, containingBlockSize); + const float positionRelativeToFlexStart = + inlineStartEdge(axis, direction) != flexStartEdge(axis) + ? getPositionOfOppositeEdge( + positionRelativeToInlineStart, axis, containingNode, child) + : positionRelativeToInlineStart; + + child->setLayoutPosition(positionRelativeToFlexStart, flexStartEdge(axis)); + } else { + isMainAxis ? justifyAbsoluteChild( + parent, child, direction, axis, containingBlockWidth) + : alignAbsoluteChild( + parent, child, direction, axis, containingBlockWidth); + } +} + +static void positionAbsoluteChild( + const yoga::Node* const containingNode, + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const bool isMainAxis, + const float containingBlockWidth, + const float containingBlockHeight) { + child->hasErrata(Errata::AbsolutePositioningIncorrect) + ? positionAbsoluteChildLegacy( + containingNode, + parent, + child, + direction, + axis, + isMainAxis, + containingBlockWidth, + containingBlockHeight) + : positionAbsoluteChildImpl( + containingNode, + parent, + child, + direction, + axis, + isMainAxis, + containingBlockWidth, + containingBlockHeight); +} + +void layoutAbsoluteChild( + const yoga::Node* const containingNode, + const yoga::Node* const node, + yoga::Node* const child, + const float containingBlockWidth, + const float containingBlockHeight, + const SizingMode widthMode, + const Direction direction, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + const FlexDirection mainAxis = + resolveDirection(node->style().flexDirection(), direction); + const FlexDirection crossAxis = resolveCrossDirection(mainAxis, direction); + const bool isMainAxisRow = isRow(mainAxis); + + float childWidth = YGUndefined; + float childHeight = YGUndefined; + SizingMode childWidthSizingMode = SizingMode::MaxContent; + SizingMode childHeightSizingMode = SizingMode::MaxContent; + + auto marginRow = child->style().computeMarginForAxis( + FlexDirection::Row, containingBlockWidth); + auto marginColumn = child->style().computeMarginForAxis( + FlexDirection::Column, containingBlockWidth); + + if (child->hasDefiniteLength(Dimension::Width, containingBlockWidth)) { + childWidth = child->getResolvedDimension(Dimension::Width) + .resolve(containingBlockWidth) + .unwrap() + + marginRow; + } else { + // If the child doesn't have a specified width, compute the width based on + // the left/right offsets if they're defined. + if (child->style().isFlexStartPositionDefined( + FlexDirection::Row, direction) && + child->style().isFlexEndPositionDefined( + FlexDirection::Row, direction)) { + childWidth = + containingNode->getLayout().measuredDimension(Dimension::Width) - + (containingNode->style().computeFlexStartBorder( + FlexDirection::Row, direction) + + containingNode->style().computeFlexEndBorder( + FlexDirection::Row, direction)) - + (child->style().computeFlexStartPosition( + FlexDirection::Row, direction, containingBlockWidth) + + child->style().computeFlexEndPosition( + FlexDirection::Row, direction, containingBlockWidth)); + childWidth = boundAxis( + child, + FlexDirection::Row, + childWidth, + containingBlockWidth, + containingBlockWidth); + } + } + + if (child->hasDefiniteLength(Dimension::Height, containingBlockHeight)) { + childHeight = child->getResolvedDimension(Dimension::Height) + .resolve(containingBlockHeight) + .unwrap() + + marginColumn; + } else { + // If the child doesn't have a specified height, compute the height based + // on the top/bottom offsets if they're defined. + if (child->style().isFlexStartPositionDefined( + FlexDirection::Column, direction) && + child->style().isFlexEndPositionDefined( + FlexDirection::Column, direction)) { + childHeight = + containingNode->getLayout().measuredDimension(Dimension::Height) - + (containingNode->style().computeFlexStartBorder( + FlexDirection::Column, direction) + + containingNode->style().computeFlexEndBorder( + FlexDirection::Column, direction)) - + (child->style().computeFlexStartPosition( + FlexDirection::Column, direction, containingBlockHeight) + + child->style().computeFlexEndPosition( + FlexDirection::Column, direction, containingBlockHeight)); + childHeight = boundAxis( + child, + FlexDirection::Column, + childHeight, + containingBlockHeight, + containingBlockWidth); + } + } + + // Exactly one dimension needs to be defined for us to be able to do aspect + // ratio calculation. One dimension being the anchor and the other being + // flexible. + const auto& childStyle = child->style(); + if (yoga::isUndefined(childWidth) ^ yoga::isUndefined(childHeight)) { + if (childStyle.aspectRatio().isDefined()) { + if (yoga::isUndefined(childWidth)) { + childWidth = marginRow + + (childHeight - marginColumn) * childStyle.aspectRatio().unwrap(); + } else if (yoga::isUndefined(childHeight)) { + childHeight = marginColumn + + (childWidth - marginRow) / childStyle.aspectRatio().unwrap(); + } + } + } + + // If we're still missing one or the other dimension, measure the content. + if (yoga::isUndefined(childWidth) || yoga::isUndefined(childHeight)) { + childWidthSizingMode = yoga::isUndefined(childWidth) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + childHeightSizingMode = yoga::isUndefined(childHeight) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + + // If the size of the owner is defined then try to constrain the absolute + // child to that size as well. This allows text within the absolute child + // to wrap to the size of its owner. This is the same behavior as many + // browsers implement. + if (!isMainAxisRow && yoga::isUndefined(childWidth) && + widthMode != SizingMode::MaxContent && + yoga::isDefined(containingBlockWidth) && containingBlockWidth > 0) { + childWidth = containingBlockWidth; + childWidthSizingMode = SizingMode::FitContent; + } + + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + childWidthSizingMode, + childHeightSizingMode, + containingBlockWidth, + containingBlockHeight, + false, + LayoutPassReason::kAbsMeasureChild, + layoutMarkerData, + depth, + generationCount); + childWidth = child->getLayout().measuredDimension(Dimension::Width) + + child->style().computeMarginForAxis( + FlexDirection::Row, containingBlockWidth); + childHeight = child->getLayout().measuredDimension(Dimension::Height) + + child->style().computeMarginForAxis( + FlexDirection::Column, containingBlockWidth); + } + + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + SizingMode::StretchFit, + SizingMode::StretchFit, + containingBlockWidth, + containingBlockHeight, + true, + LayoutPassReason::kAbsLayout, + layoutMarkerData, + depth, + generationCount); + + positionAbsoluteChild( + containingNode, + node, + child, + direction, + mainAxis, + true /*isMainAxis*/, + containingBlockWidth, + containingBlockHeight); + positionAbsoluteChild( + containingNode, + node, + child, + direction, + crossAxis, + false /*isMainAxis*/, + containingBlockWidth, + containingBlockHeight); +} + +void layoutAbsoluteDescendants( + yoga::Node* containingNode, + yoga::Node* currentNode, + SizingMode widthSizingMode, + Direction currentNodeDirection, + LayoutData& layoutMarkerData, + uint32_t currentDepth, + uint32_t generationCount, + float currentNodeLeftOffsetFromContainingBlock, + float currentNodeTopOffsetFromContainingBlock, + float containingNodeAvailableInnerWidth, + float containingNodeAvailableInnerHeight) { + for (auto child : currentNode->getChildren()) { + if (child->style().display() == Display::None) { + continue; + } else if (child->style().positionType() == PositionType::Absolute) { + const bool absoluteErrata = + currentNode->hasErrata(Errata::AbsolutePercentAgainstInnerSize); + const float containingBlockWidth = absoluteErrata + ? containingNodeAvailableInnerWidth + : containingNode->getLayout().measuredDimension(Dimension::Width) - + containingNode->style().computeBorderForAxis(FlexDirection::Row); + const float containingBlockHeight = absoluteErrata + ? containingNodeAvailableInnerHeight + : containingNode->getLayout().measuredDimension(Dimension::Height) - + containingNode->style().computeBorderForAxis( + FlexDirection::Column); + + layoutAbsoluteChild( + containingNode, + currentNode, + child, + containingBlockWidth, + containingBlockHeight, + widthSizingMode, + currentNodeDirection, + layoutMarkerData, + currentDepth, + generationCount); + + /* + * At this point the child has its position set but only on its the + * parent's flexStart edge. Additionally, this position should be + * interpreted relative to the containing block of the child if it had + * insets defined. So we need to adjust the position by subtracting the + * the parents offset from the containing block. However, getting that + * offset is complicated since the two nodes can have different main/cross + * axes. + */ + const FlexDirection parentMainAxis = resolveDirection( + currentNode->style().flexDirection(), currentNodeDirection); + const FlexDirection parentCrossAxis = + resolveCrossDirection(parentMainAxis, currentNodeDirection); + + if (needsTrailingPosition(parentMainAxis)) { + const bool mainInsetsDefined = isRow(parentMainAxis) + ? child->style().horizontalInsetsDefined() + : child->style().verticalInsetsDefined(); + setChildTrailingPosition( + mainInsetsDefined ? containingNode : currentNode, + child, + parentMainAxis); + } + if (needsTrailingPosition(parentCrossAxis)) { + const bool crossInsetsDefined = isRow(parentCrossAxis) + ? child->style().horizontalInsetsDefined() + : child->style().verticalInsetsDefined(); + setChildTrailingPosition( + crossInsetsDefined ? containingNode : currentNode, + child, + parentCrossAxis); + } + + /* + * At this point we know the left and top physical edges of the child are + * set with positions that are relative to the containing block if insets + * are defined + */ + const float childLeftPosition = + child->getLayout().position(PhysicalEdge::Left); + const float childTopPosition = + child->getLayout().position(PhysicalEdge::Top); + + const float childLeftOffsetFromParent = + child->style().horizontalInsetsDefined() + ? (childLeftPosition - currentNodeLeftOffsetFromContainingBlock) + : childLeftPosition; + const float childTopOffsetFromParent = + child->style().verticalInsetsDefined() + ? (childTopPosition - currentNodeTopOffsetFromContainingBlock) + : childTopPosition; + + child->setLayoutPosition(childLeftOffsetFromParent, PhysicalEdge::Left); + child->setLayoutPosition(childTopOffsetFromParent, PhysicalEdge::Top); + } else if ( + child->style().positionType() == PositionType::Static && + !child->alwaysFormsContainingBlock()) { + const Direction childDirection = + child->resolveDirection(currentNodeDirection); + // By now all descendants of the containing block that are not absolute + // will have their positions set for left and top. + const float childLeftOffsetFromContainingBlock = + currentNodeLeftOffsetFromContainingBlock + + child->getLayout().position(PhysicalEdge::Left); + const float childTopOffsetFromContainingBlock = + currentNodeTopOffsetFromContainingBlock + + child->getLayout().position(PhysicalEdge::Top); + + layoutAbsoluteDescendants( + containingNode, + child, + widthSizingMode, + childDirection, + layoutMarkerData, + currentDepth + 1, + generationCount, + childLeftOffsetFromContainingBlock, + childTopOffsetFromContainingBlock, + containingNodeAvailableInnerWidth, + containingNodeAvailableInnerHeight); + } + } +} +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/AbsoluteLayout.h b/Source/3rdParty/yoga/algorithm/AbsoluteLayout.h new file mode 100644 index 000000000..164cd0cff --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/AbsoluteLayout.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/event/event.h" +#include "yoga/node/Node.h" + +namespace facebook::yoga { + +void layoutAbsoluteChild( + const yoga::Node* containingNode, + const yoga::Node* node, + yoga::Node* child, + float containingBlockWidth, + float containingBlockHeight, + SizingMode widthMode, + Direction direction, + LayoutData& layoutMarkerData, + uint32_t depth, + uint32_t generationCount); + +void layoutAbsoluteDescendants( + yoga::Node* containingNode, + yoga::Node* currentNode, + SizingMode widthSizingMode, + Direction currentNodeDirection, + LayoutData& layoutMarkerData, + uint32_t currentDepth, + uint32_t generationCount, + float currentNodeMainOffsetFromContainingBlock, + float currentNodeCrossOffsetFromContainingBlock, + float containingNodeAvailableInnerWidth, + float containingNodeAvailableInnerHeight); + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/Align.h b/Source/3rdParty/yoga/algorithm/Align.h new file mode 100644 index 000000000..a222f9a99 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/Align.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/Yoga.h" + +#include "yoga/algorithm/FlexDirection.h" +#include "yoga/node/Node.h" + +namespace facebook::yoga { + +inline Align resolveChildAlignment( + const yoga::Node* node, + const yoga::Node* child) { + const Align align = child->style().alignSelf() == Align::Auto + ? node->style().alignItems() + : child->style().alignSelf(); + if (align == Align::Baseline && isColumn(node->style().flexDirection())) { + return Align::FlexStart; + } + return align; +} + +/** + * Fallback alignment to use on overflow + * https://www.w3.org/TR/css-align-3/#distribution-values + */ +constexpr Align fallbackAlignment(Align align) { + switch (align) { + // Fallback to flex-start + case Align::SpaceBetween: + case Align::Stretch: + return Align::FlexStart; + + // Fallback to safe center. TODO: This should be aligned to Start + // instead of FlexStart (for row-reverse containers) + case Align::SpaceAround: + case Align::SpaceEvenly: + return Align::FlexStart; + default: + return align; + } +} + +/** + * Fallback alignment to use on overflow + * https://www.w3.org/TR/css-align-3/#distribution-values + */ +constexpr Justify fallbackAlignment(Justify align) { + switch (align) { + // Fallback to flex-start + case Justify::SpaceBetween: + // TODO: Support `justify-content: stretch` + // case Justify::Stretch: + return Justify::FlexStart; + + // Fallback to safe center. TODO: This should be aligned to Start + // instead of FlexStart (for row-reverse containers) + case Justify::SpaceAround: + case Justify::SpaceEvenly: + return Justify::FlexStart; + default: + return align; + } +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/Baseline.cpp b/Source/3rdParty/yoga/algorithm/Baseline.cpp new file mode 100644 index 000000000..eb0fbc64e --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/Baseline.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/Yoga.h" + +#include "yoga/algorithm/Align.h" +#include "yoga/algorithm/Baseline.h" +#include "yoga/debug/AssertFatal.h" +#include "yoga/event/event.h" + +namespace facebook::yoga { + +float calculateBaseline(const yoga::Node* node) { + if (node->hasBaselineFunc()) { + Event::publish(node); + + const float baseline = node->baseline( + node->getLayout().measuredDimension(Dimension::Width), + node->getLayout().measuredDimension(Dimension::Height)); + + Event::publish(node); + + yoga::assertFatalWithNode( + node, + !std::isnan(baseline), + "Expect custom baseline function to not return NaN"); + return baseline; + } + + yoga::Node* baselineChild = nullptr; + const size_t childCount = node->getChildCount(); + for (size_t i = 0; i < childCount; i++) { + auto child = node->getChild(i); + if (child->getLineIndex() > 0) { + break; + } + if (child->style().positionType() == PositionType::Absolute) { + continue; + } + if (resolveChildAlignment(node, child) == Align::Baseline || + child->isReferenceBaseline()) { + baselineChild = child; + break; + } + + if (baselineChild == nullptr) { + baselineChild = child; + } + } + + if (baselineChild == nullptr) { + return node->getLayout().measuredDimension(Dimension::Height); + } + + const float baseline = calculateBaseline(baselineChild); + return baseline + baselineChild->getLayout().position(PhysicalEdge::Top); +} + +bool isBaselineLayout(const yoga::Node* node) { + if (isColumn(node->style().flexDirection())) { + return false; + } + if (node->style().alignItems() == Align::Baseline) { + return true; + } + const auto childCount = node->getChildCount(); + for (size_t i = 0; i < childCount; i++) { + auto child = node->getChild(i); + if (child->style().positionType() != PositionType::Absolute && + child->style().alignSelf() == Align::Baseline) { + return true; + } + } + + return false; +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/Baseline.h b/Source/3rdParty/yoga/algorithm/Baseline.h new file mode 100644 index 000000000..ba9c79b06 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/Baseline.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/Yoga.h" +#include "yoga/node/Node.h" + +namespace facebook::yoga { + +// Calculate baseline represented as an offset from the top edge of the node. +float calculateBaseline(const yoga::Node* node); + +// Whether any of the children of this node participate in baseline alignment +bool isBaselineLayout(const yoga::Node* node); + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/BoundAxis.h b/Source/3rdParty/yoga/algorithm/BoundAxis.h new file mode 100644 index 000000000..437ff93ae --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/BoundAxis.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/algorithm/FlexDirection.h" +#include "yoga/enums/Dimension.h" +#include "yoga/enums/FlexDirection.h" +#include "yoga/node/Node.h" +#include "yoga/numeric/Comparison.h" +#include "yoga/numeric/FloatOptional.h" + +namespace facebook::yoga { + +inline float paddingAndBorderForAxis( + const yoga::Node* const node, + const FlexDirection axis, + const float widthSize) { + // The total padding/border for a given axis does not depend on the direction + // so hardcoding LTR here to avoid piping direction to this function + return node->style().computeInlineStartPaddingAndBorder( + axis, Direction::LTR, widthSize) + + node->style().computeInlineEndPaddingAndBorder( + axis, Direction::LTR, widthSize); +} + +inline FloatOptional boundAxisWithinMinAndMax( + const yoga::Node* const node, + const FlexDirection axis, + const FloatOptional value, + const float axisSize) { + FloatOptional min; + FloatOptional max; + + if (isColumn(axis)) { + min = node->style().minDimension(Dimension::Height).resolve(axisSize); + max = node->style().maxDimension(Dimension::Height).resolve(axisSize); + } else if (isRow(axis)) { + min = node->style().minDimension(Dimension::Width).resolve(axisSize); + max = node->style().maxDimension(Dimension::Width).resolve(axisSize); + } + + if (max >= FloatOptional{0} && value > max) { + return max; + } + + if (min >= FloatOptional{0} && value < min) { + return min; + } + + return value; +} + +// Like boundAxisWithinMinAndMax but also ensures that the value doesn't +// go below the padding and border amount. +inline float boundAxis( + const yoga::Node* const node, + const FlexDirection axis, + const float value, + const float axisSize, + const float widthSize) { + return yoga::maxOrDefined( + boundAxisWithinMinAndMax(node, axis, FloatOptional{value}, axisSize) + .unwrap(), + paddingAndBorderForAxis(node, axis, widthSize)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/Cache.cpp b/Source/3rdParty/yoga/algorithm/Cache.cpp new file mode 100644 index 000000000..d58d0ee33 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/Cache.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/algorithm/Cache.h" +#include "yoga/algorithm/PixelGrid.h" +#include "yoga/numeric/Comparison.h" + +namespace facebook::yoga { + +static inline bool sizeIsExactAndMatchesOldMeasuredSize( + SizingMode sizeMode, + float size, + float lastComputedSize) { + return sizeMode == SizingMode::StretchFit && + yoga::inexactEquals(size, lastComputedSize); +} + +static inline bool oldSizeIsMaxContentAndStillFits( + SizingMode sizeMode, + float size, + SizingMode lastSizeMode, + float lastComputedSize) { + return sizeMode == SizingMode::FitContent && + lastSizeMode == SizingMode::MaxContent && + (size >= lastComputedSize || yoga::inexactEquals(size, lastComputedSize)); +} + +static inline bool newSizeIsStricterAndStillValid( + SizingMode sizeMode, + float size, + SizingMode lastSizeMode, + float lastSize, + float lastComputedSize) { + return lastSizeMode == SizingMode::FitContent && + sizeMode == SizingMode::FitContent && yoga::isDefined(lastSize) && + yoga::isDefined(size) && yoga::isDefined(lastComputedSize) && + lastSize > size && + (lastComputedSize <= size || yoga::inexactEquals(size, lastComputedSize)); +} + +bool canUseCachedMeasurement( + const SizingMode widthMode, + const float availableWidth, + const SizingMode heightMode, + const float availableHeight, + const SizingMode lastWidthMode, + const float lastAvailableWidth, + const SizingMode lastHeightMode, + const float lastAvailableHeight, + const float lastComputedWidth, + const float lastComputedHeight, + const float marginRow, + const float marginColumn, + const yoga::Config* const config) { + if ((yoga::isDefined(lastComputedHeight) && lastComputedHeight < 0) || + ((yoga::isDefined(lastComputedWidth)) && lastComputedWidth < 0)) { + return false; + } + + const float pointScaleFactor = config->getPointScaleFactor(); + + bool useRoundedComparison = config != nullptr && pointScaleFactor != 0; + const float effectiveWidth = useRoundedComparison + ? roundValueToPixelGrid(availableWidth, pointScaleFactor, false, false) + : availableWidth; + const float effectiveHeight = useRoundedComparison + ? roundValueToPixelGrid(availableHeight, pointScaleFactor, false, false) + : availableHeight; + const float effectiveLastWidth = useRoundedComparison + ? roundValueToPixelGrid( + lastAvailableWidth, pointScaleFactor, false, false) + : lastAvailableWidth; + const float effectiveLastHeight = useRoundedComparison + ? roundValueToPixelGrid( + lastAvailableHeight, pointScaleFactor, false, false) + : lastAvailableHeight; + + const bool hasSameWidthSpec = lastWidthMode == widthMode && + yoga::inexactEquals(effectiveLastWidth, effectiveWidth); + const bool hasSameHeightSpec = lastHeightMode == heightMode && + yoga::inexactEquals(effectiveLastHeight, effectiveHeight); + + const bool widthIsCompatible = + hasSameWidthSpec || + sizeIsExactAndMatchesOldMeasuredSize( + widthMode, availableWidth - marginRow, lastComputedWidth) || + oldSizeIsMaxContentAndStillFits( + widthMode, + availableWidth - marginRow, + lastWidthMode, + lastComputedWidth) || + newSizeIsStricterAndStillValid( + widthMode, + availableWidth - marginRow, + lastWidthMode, + lastAvailableWidth, + lastComputedWidth); + + const bool heightIsCompatible = hasSameHeightSpec || + sizeIsExactAndMatchesOldMeasuredSize( + heightMode, + availableHeight - marginColumn, + lastComputedHeight) || + oldSizeIsMaxContentAndStillFits(heightMode, + availableHeight - marginColumn, + lastHeightMode, + lastComputedHeight) || + newSizeIsStricterAndStillValid(heightMode, + availableHeight - marginColumn, + lastHeightMode, + lastAvailableHeight, + lastComputedHeight); + + return widthIsCompatible && heightIsCompatible; +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/Cache.h b/Source/3rdParty/yoga/algorithm/Cache.h new file mode 100644 index 000000000..43543f2c3 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/Cache.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/algorithm/SizingMode.h" +#include "yoga/config/Config.h" + +namespace facebook::yoga { + +bool canUseCachedMeasurement( + SizingMode widthMode, + float availableWidth, + SizingMode heightMode, + float availableHeight, + SizingMode lastWidthMode, + float lastAvailableWidth, + SizingMode lastHeightMode, + float lastAvailableHeight, + float lastComputedWidth, + float lastComputedHeight, + float marginRow, + float marginColumn, + const yoga::Config* config); + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/CalculateLayout.cpp b/Source/3rdParty/yoga/algorithm/CalculateLayout.cpp new file mode 100644 index 000000000..194eecbf1 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/CalculateLayout.cpp @@ -0,0 +1,2353 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include +#include +#include + +#include "yoga/Yoga.h" + +#include "yoga/algorithm/AbsoluteLayout.h" +#include "yoga/algorithm/Align.h" +#include "yoga/algorithm/Baseline.h" +#include "yoga/algorithm/BoundAxis.h" +#include "yoga/algorithm/Cache.h" +#include "yoga/algorithm/CalculateLayout.h" +#include "yoga/algorithm/FlexDirection.h" +#include "yoga/algorithm/FlexLine.h" +#include "yoga/algorithm/PixelGrid.h" +#include "yoga/algorithm/SizingMode.h" +#include "yoga/algorithm/TrailingPosition.h" +#include "yoga/debug/AssertFatal.h" +#include "yoga/debug/Log.h" +#include "yoga/event/event.h" +#include "yoga/node/Node.h" +#include "yoga/numeric/Comparison.h" +#include "yoga/numeric/FloatOptional.h" + +namespace facebook::yoga { + +std::atomic gCurrentGenerationCount(0); + +static void constrainMaxSizeForMode( + const yoga::Node* node, + FlexDirection axis, + float ownerAxisSize, + float ownerWidth, + /*in_out*/ SizingMode* mode, + /*in_out*/ float* size) { + const FloatOptional maxSize = + node->style().maxDimension(dimension(axis)).resolve(ownerAxisSize) + + FloatOptional(node->style().computeMarginForAxis(axis, ownerWidth)); + switch (*mode) { + case SizingMode::StretchFit: + case SizingMode::FitContent: + *size = (maxSize.isUndefined() || *size < maxSize.unwrap()) + ? *size + : maxSize.unwrap(); + break; + case SizingMode::MaxContent: + if (maxSize.isDefined()) { + *mode = SizingMode::FitContent; + *size = maxSize.unwrap(); + } + break; + } +} + +static void computeFlexBasisForChild( + const yoga::Node* const node, + yoga::Node* const child, + const float width, + const SizingMode widthMode, + const float height, + const float ownerWidth, + const float ownerHeight, + const SizingMode heightMode, + const Direction direction, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + const FlexDirection mainAxis = + resolveDirection(node->style().flexDirection(), direction); + const bool isMainAxisRow = isRow(mainAxis); + const float mainAxisSize = isMainAxisRow ? width : height; + const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight; + + float childWidth = YGUndefined; + float childHeight = YGUndefined; + SizingMode childWidthSizingMode; + SizingMode childHeightSizingMode; + + const FloatOptional resolvedFlexBasis = + child->resolveFlexBasisPtr().resolve(mainAxisownerSize); + const bool isRowStyleDimDefined = + child->hasDefiniteLength(Dimension::Width, ownerWidth); + const bool isColumnStyleDimDefined = + child->hasDefiniteLength(Dimension::Height, ownerHeight); + + if (resolvedFlexBasis.isDefined() && yoga::isDefined(mainAxisSize)) { + if (child->getLayout().computedFlexBasis.isUndefined() || + (child->getConfig()->isExperimentalFeatureEnabled( + ExperimentalFeature::WebFlexBasis) && + child->getLayout().computedFlexBasisGeneration != generationCount)) { + const FloatOptional paddingAndBorder = + FloatOptional(paddingAndBorderForAxis(child, mainAxis, ownerWidth)); + child->setLayoutComputedFlexBasis( + yoga::maxOrDefined(resolvedFlexBasis, paddingAndBorder)); + } + } else if (isMainAxisRow && isRowStyleDimDefined) { + // The width is definite, so use that as the flex basis. + const FloatOptional paddingAndBorder = FloatOptional( + paddingAndBorderForAxis(child, FlexDirection::Row, ownerWidth)); + + child->setLayoutComputedFlexBasis(yoga::maxOrDefined( + child->getResolvedDimension(Dimension::Width).resolve(ownerWidth), + paddingAndBorder)); + } else if (!isMainAxisRow && isColumnStyleDimDefined) { + // The height is definite, so use that as the flex basis. + const FloatOptional paddingAndBorder = FloatOptional( + paddingAndBorderForAxis(child, FlexDirection::Column, ownerWidth)); + child->setLayoutComputedFlexBasis(yoga::maxOrDefined( + child->getResolvedDimension(Dimension::Height).resolve(ownerHeight), + paddingAndBorder)); + } else { + // Compute the flex basis and hypothetical main size (i.e. the clamped flex + // basis). + childWidthSizingMode = SizingMode::MaxContent; + childHeightSizingMode = SizingMode::MaxContent; + + auto marginRow = + child->style().computeMarginForAxis(FlexDirection::Row, ownerWidth); + auto marginColumn = + child->style().computeMarginForAxis(FlexDirection::Column, ownerWidth); + + if (isRowStyleDimDefined) { + childWidth = child->getResolvedDimension(Dimension::Width) + .resolve(ownerWidth) + .unwrap() + + marginRow; + childWidthSizingMode = SizingMode::StretchFit; + } + if (isColumnStyleDimDefined) { + childHeight = child->getResolvedDimension(Dimension::Height) + .resolve(ownerHeight) + .unwrap() + + marginColumn; + childHeightSizingMode = SizingMode::StretchFit; + } + + // The W3C spec doesn't say anything about the 'overflow' property, but all + // major browsers appear to implement the following logic. + if ((!isMainAxisRow && node->style().overflow() == Overflow::Scroll) || + node->style().overflow() != Overflow::Scroll) { + if (yoga::isUndefined(childWidth) && yoga::isDefined(width)) { + childWidth = width; + childWidthSizingMode = SizingMode::FitContent; + } + } + + if ((isMainAxisRow && node->style().overflow() == Overflow::Scroll) || + node->style().overflow() != Overflow::Scroll) { + if (yoga::isUndefined(childHeight) && yoga::isDefined(height)) { + childHeight = height; + childHeightSizingMode = SizingMode::FitContent; + } + } + + const auto& childStyle = child->style(); + if (childStyle.aspectRatio().isDefined()) { + if (!isMainAxisRow && childWidthSizingMode == SizingMode::StretchFit) { + childHeight = marginColumn + + (childWidth - marginRow) / childStyle.aspectRatio().unwrap(); + childHeightSizingMode = SizingMode::StretchFit; + } else if ( + isMainAxisRow && childHeightSizingMode == SizingMode::StretchFit) { + childWidth = marginRow + + (childHeight - marginColumn) * childStyle.aspectRatio().unwrap(); + childWidthSizingMode = SizingMode::StretchFit; + } + } + + // If child has no defined size in the cross axis and is set to stretch, set + // the cross axis to be measured exactly with the available inner width + + const bool hasExactWidth = + yoga::isDefined(width) && widthMode == SizingMode::StretchFit; + const bool childWidthStretch = + resolveChildAlignment(node, child) == Align::Stretch && + childWidthSizingMode != SizingMode::StretchFit; + if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth && + childWidthStretch) { + childWidth = width; + childWidthSizingMode = SizingMode::StretchFit; + if (childStyle.aspectRatio().isDefined()) { + childHeight = + (childWidth - marginRow) / childStyle.aspectRatio().unwrap(); + childHeightSizingMode = SizingMode::StretchFit; + } + } + + const bool hasExactHeight = + yoga::isDefined(height) && heightMode == SizingMode::StretchFit; + const bool childHeightStretch = + resolveChildAlignment(node, child) == Align::Stretch && + childHeightSizingMode != SizingMode::StretchFit; + if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight && + childHeightStretch) { + childHeight = height; + childHeightSizingMode = SizingMode::StretchFit; + + if (childStyle.aspectRatio().isDefined()) { + childWidth = + (childHeight - marginColumn) * childStyle.aspectRatio().unwrap(); + childWidthSizingMode = SizingMode::StretchFit; + } + } + + constrainMaxSizeForMode( + child, + FlexDirection::Row, + ownerWidth, + ownerWidth, + &childWidthSizingMode, + &childWidth); + constrainMaxSizeForMode( + child, + FlexDirection::Column, + ownerHeight, + ownerWidth, + &childHeightSizingMode, + &childHeight); + + // Measure the child + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + childWidthSizingMode, + childHeightSizingMode, + ownerWidth, + ownerHeight, + false, + LayoutPassReason::kMeasureChild, + layoutMarkerData, + depth, + generationCount); + + child->setLayoutComputedFlexBasis(FloatOptional(yoga::maxOrDefined( + child->getLayout().measuredDimension(dimension(mainAxis)), + paddingAndBorderForAxis(child, mainAxis, ownerWidth)))); + } + child->setLayoutComputedFlexBasisGeneration(generationCount); +} + +static void measureNodeWithMeasureFunc( + yoga::Node* const node, + float availableWidth, + float availableHeight, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight, + LayoutData& layoutMarkerData, + const LayoutPassReason reason) { + yoga::assertFatalWithNode( + node, + node->hasMeasureFunc(), + "Expected node to have custom measure function"); + + if (widthSizingMode == SizingMode::MaxContent) { + availableWidth = YGUndefined; + } + if (heightSizingMode == SizingMode::MaxContent) { + availableHeight = YGUndefined; + } + + const auto& layout = node->getLayout(); + const float paddingAndBorderAxisRow = layout.padding(PhysicalEdge::Left) + + layout.padding(PhysicalEdge::Right) + layout.border(PhysicalEdge::Left) + + layout.border(PhysicalEdge::Right); + const float paddingAndBorderAxisColumn = layout.padding(PhysicalEdge::Top) + + layout.padding(PhysicalEdge::Bottom) + layout.border(PhysicalEdge::Top) + + layout.border(PhysicalEdge::Bottom); + + // We want to make sure we don't call measure with negative size + const float innerWidth = yoga::isUndefined(availableWidth) + ? availableWidth + : yoga::maxOrDefined(0.0f, availableWidth - paddingAndBorderAxisRow); + const float innerHeight = yoga::isUndefined(availableHeight) + ? availableHeight + : yoga::maxOrDefined(0.0f, availableHeight - paddingAndBorderAxisColumn); + + if (widthSizingMode == SizingMode::StretchFit && + heightSizingMode == SizingMode::StretchFit) { + // Don't bother sizing the text if both dimensions are already defined. + node->setLayoutMeasuredDimension( + boundAxis( + node, FlexDirection::Row, availableWidth, ownerWidth, ownerWidth), + Dimension::Width); + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Column, + availableHeight, + ownerHeight, + ownerWidth), + Dimension::Height); + } else { + Event::publish(node); + + // Measure the text under the current constraints. + const YGSize measuredSize = node->measure( + innerWidth, + measureMode(widthSizingMode), + innerHeight, + measureMode(heightSizingMode)); + + layoutMarkerData.measureCallbacks += 1; + layoutMarkerData.measureCallbackReasonsCount[static_cast(reason)] += + 1; + + Event::publish( + node, + {innerWidth, + unscopedEnum(measureMode(widthSizingMode)), + innerHeight, + unscopedEnum(measureMode(heightSizingMode)), + measuredSize.width, + measuredSize.height, + reason}); + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Row, + (widthSizingMode == SizingMode::MaxContent || + widthSizingMode == SizingMode::FitContent) + ? measuredSize.width + paddingAndBorderAxisRow + : availableWidth, + ownerWidth, + ownerWidth), + Dimension::Width); + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Column, + (heightSizingMode == SizingMode::MaxContent || + heightSizingMode == SizingMode::FitContent) + ? measuredSize.height + paddingAndBorderAxisColumn + : availableHeight, + ownerHeight, + ownerWidth), + Dimension::Height); + } +} + +// For nodes with no children, use the available values if they were provided, +// or the minimum size as indicated by the padding and border sizes. +static void measureNodeWithoutChildren( + yoga::Node* const node, + const float availableWidth, + const float availableHeight, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight) { + const auto& layout = node->getLayout(); + + float width = availableWidth; + if (widthSizingMode == SizingMode::MaxContent || + widthSizingMode == SizingMode::FitContent) { + width = layout.padding(PhysicalEdge::Left) + + layout.padding(PhysicalEdge::Right) + + layout.border(PhysicalEdge::Left) + layout.border(PhysicalEdge::Right); + } + node->setLayoutMeasuredDimension( + boundAxis(node, FlexDirection::Row, width, ownerWidth, ownerWidth), + Dimension::Width); + + float height = availableHeight; + if (heightSizingMode == SizingMode::MaxContent || + heightSizingMode == SizingMode::FitContent) { + height = layout.padding(PhysicalEdge::Top) + + layout.padding(PhysicalEdge::Bottom) + + layout.border(PhysicalEdge::Top) + layout.border(PhysicalEdge::Bottom); + } + node->setLayoutMeasuredDimension( + boundAxis(node, FlexDirection::Column, height, ownerHeight, ownerWidth), + Dimension::Height); +} + +static bool measureNodeWithFixedSize( + yoga::Node* const node, + const float availableWidth, + const float availableHeight, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight) { + if ((yoga::isDefined(availableWidth) && + widthSizingMode == SizingMode::FitContent && availableWidth <= 0.0f) || + (yoga::isDefined(availableHeight) && + heightSizingMode == SizingMode::FitContent && availableHeight <= 0.0f) || + (widthSizingMode == SizingMode::StretchFit && + heightSizingMode == SizingMode::StretchFit)) { + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Row, + yoga::isUndefined(availableWidth) || + (widthSizingMode == SizingMode::FitContent && + availableWidth < 0.0f) + ? 0.0f + : availableWidth, + ownerWidth, + ownerWidth), + Dimension::Width); + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Column, + yoga::isUndefined(availableHeight) || + (heightSizingMode == SizingMode::FitContent && + availableHeight < 0.0f) + ? 0.0f + : availableHeight, + ownerHeight, + ownerWidth), + Dimension::Height); + return true; + } + + return false; +} + +static void zeroOutLayoutRecursively(yoga::Node* const node) { + node->getLayout() = {}; + node->setLayoutDimension(0, Dimension::Width); + node->setLayoutDimension(0, Dimension::Height); + node->setHasNewLayout(true); + + node->cloneChildrenIfNeeded(); + for (const auto child : node->getChildren()) { + zeroOutLayoutRecursively(child); + } +} + +static float calculateAvailableInnerDimension( + const yoga::Node* const node, + const Dimension dimension, + const float availableDim, + const float paddingAndBorder, + const float ownerDim) { + float availableInnerDim = availableDim - paddingAndBorder; + // Max dimension overrides predefined dimension value; Min dimension in turn + // overrides both of the above + if (yoga::isDefined(availableInnerDim)) { + // We want to make sure our available height does not violate min and max + // constraints + const FloatOptional minDimensionOptional = + node->style().minDimension(dimension).resolve(ownerDim); + const float minInnerDim = minDimensionOptional.isUndefined() + ? 0.0f + : minDimensionOptional.unwrap() - paddingAndBorder; + + const FloatOptional maxDimensionOptional = + node->style().maxDimension(dimension).resolve(ownerDim); + + const float maxInnerDim = maxDimensionOptional.isUndefined() + ? FLT_MAX + : maxDimensionOptional.unwrap() - paddingAndBorder; + availableInnerDim = yoga::maxOrDefined( + yoga::minOrDefined(availableInnerDim, maxInnerDim), minInnerDim); + } + + return availableInnerDim; +} + +static float computeFlexBasisForChildren( + yoga::Node* const node, + const float availableInnerWidth, + const float availableInnerHeight, + SizingMode widthSizingMode, + SizingMode heightSizingMode, + Direction direction, + FlexDirection mainAxis, + bool performLayout, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + float totalOuterFlexBasis = 0.0f; + YGNodeRef singleFlexChild = nullptr; + const auto& children = node->getChildren(); + SizingMode sizingModeMainDim = + isRow(mainAxis) ? widthSizingMode : heightSizingMode; + // If there is only one child with flexGrow + flexShrink it means we can set + // the computedFlexBasis to 0 instead of measuring and shrinking / flexing the + // child to exactly match the remaining space + if (sizingModeMainDim == SizingMode::StretchFit) { + for (auto child : children) { + if (child->isNodeFlexible()) { + if (singleFlexChild != nullptr || + yoga::inexactEquals(child->resolveFlexGrow(), 0.0f) || + yoga::inexactEquals(child->resolveFlexShrink(), 0.0f)) { + // There is already a flexible child, or this flexible child doesn't + // have flexGrow and flexShrink, abort + singleFlexChild = nullptr; + break; + } else { + singleFlexChild = child; + } + } + } + } + + for (auto child : children) { + child->resolveDimension(); + if (child->style().display() == Display::None) { + zeroOutLayoutRecursively(child); + child->setHasNewLayout(true); + child->setDirty(false); + continue; + } + if (performLayout) { + // Set the initial position (relative to the owner). + const Direction childDirection = child->resolveDirection(direction); + const float mainDim = + isRow(mainAxis) ? availableInnerWidth : availableInnerHeight; + const float crossDim = + isRow(mainAxis) ? availableInnerHeight : availableInnerWidth; + child->setPosition( + childDirection, mainDim, crossDim, availableInnerWidth); + } + + if (child->style().positionType() == PositionType::Absolute) { + continue; + } + if (child == singleFlexChild) { + child->setLayoutComputedFlexBasisGeneration(generationCount); + child->setLayoutComputedFlexBasis(FloatOptional(0)); + } else { + computeFlexBasisForChild( + node, + child, + availableInnerWidth, + widthSizingMode, + availableInnerHeight, + availableInnerWidth, + availableInnerHeight, + heightSizingMode, + direction, + layoutMarkerData, + depth, + generationCount); + } + + totalOuterFlexBasis += + (child->getLayout().computedFlexBasis.unwrap() + + child->style().computeMarginForAxis(mainAxis, availableInnerWidth)); + } + + return totalOuterFlexBasis; +} + +// It distributes the free space to the flexible items and ensures that the size +// of the flex items abide the min and max constraints. At the end of this +// function the child nodes would have proper size. Prior using this function +// please ensure that distributeFreeSpaceFirstPass is called. +static float distributeFreeSpaceSecondPass( + FlexLine& flexLine, + yoga::Node* const node, + const FlexDirection mainAxis, + const FlexDirection crossAxis, + const Direction direction, + const float mainAxisownerSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool mainAxisOverflows, + const SizingMode sizingModeCrossDim, + const bool performLayout, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + float childFlexBasis = 0; + float flexShrinkScaledFactor = 0; + float flexGrowFactor = 0; + float deltaFreeSpace = 0; + const bool isMainAxisRow = isRow(mainAxis); + const bool isNodeFlexWrap = node->style().flexWrap() != Wrap::NoWrap; + + for (auto currentLineChild : flexLine.itemsInFlow) { + childFlexBasis = boundAxisWithinMinAndMax( + currentLineChild, + mainAxis, + currentLineChild->getLayout().computedFlexBasis, + mainAxisownerSize) + .unwrap(); + float updatedMainSize = childFlexBasis; + + if (yoga::isDefined(flexLine.layout.remainingFreeSpace) && + flexLine.layout.remainingFreeSpace < 0) { + flexShrinkScaledFactor = + -currentLineChild->resolveFlexShrink() * childFlexBasis; + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + float childSize = YGUndefined; + + if (yoga::isDefined(flexLine.layout.totalFlexShrinkScaledFactors) && + flexLine.layout.totalFlexShrinkScaledFactors == 0) { + childSize = childFlexBasis + flexShrinkScaledFactor; + } else { + childSize = childFlexBasis + + (flexLine.layout.remainingFreeSpace / + flexLine.layout.totalFlexShrinkScaledFactors) * + flexShrinkScaledFactor; + } + + updatedMainSize = boundAxis( + currentLineChild, + mainAxis, + childSize, + availableInnerMainDim, + availableInnerWidth); + } + } else if ( + yoga::isDefined(flexLine.layout.remainingFreeSpace) && + flexLine.layout.remainingFreeSpace > 0) { + flexGrowFactor = currentLineChild->resolveFlexGrow(); + + // Is this child able to grow? + if (!std::isnan(flexGrowFactor) && flexGrowFactor != 0) { + updatedMainSize = boundAxis( + currentLineChild, + mainAxis, + childFlexBasis + + flexLine.layout.remainingFreeSpace / + flexLine.layout.totalFlexGrowFactors * flexGrowFactor, + availableInnerMainDim, + availableInnerWidth); + } + } + + deltaFreeSpace += updatedMainSize - childFlexBasis; + + const float marginMain = currentLineChild->style().computeMarginForAxis( + mainAxis, availableInnerWidth); + const float marginCross = currentLineChild->style().computeMarginForAxis( + crossAxis, availableInnerWidth); + + float childCrossSize = YGUndefined; + float childMainSize = updatedMainSize + marginMain; + SizingMode childCrossSizingMode; + SizingMode childMainSizingMode = SizingMode::StretchFit; + + const auto& childStyle = currentLineChild->style(); + if (childStyle.aspectRatio().isDefined()) { + childCrossSize = isMainAxisRow + ? (childMainSize - marginMain) / childStyle.aspectRatio().unwrap() + : (childMainSize - marginMain) * childStyle.aspectRatio().unwrap(); + childCrossSizingMode = SizingMode::StretchFit; + + childCrossSize += marginCross; + } else if ( + !std::isnan(availableInnerCrossDim) && + !currentLineChild->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim) && + sizingModeCrossDim == SizingMode::StretchFit && + !(isNodeFlexWrap && mainAxisOverflows) && + resolveChildAlignment(node, currentLineChild) == Align::Stretch && + !currentLineChild->style().flexStartMarginIsAuto( + crossAxis, direction) && + !currentLineChild->style().flexEndMarginIsAuto(crossAxis, direction)) { + childCrossSize = availableInnerCrossDim; + childCrossSizingMode = SizingMode::StretchFit; + } else if (!currentLineChild->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim)) { + childCrossSize = availableInnerCrossDim; + childCrossSizingMode = yoga::isUndefined(childCrossSize) + ? SizingMode::MaxContent + : SizingMode::FitContent; + } else { + childCrossSize = + currentLineChild->getResolvedDimension(dimension(crossAxis)) + .resolve(availableInnerCrossDim) + .unwrap() + + marginCross; + const bool isLoosePercentageMeasurement = + currentLineChild->getResolvedDimension(dimension(crossAxis)).unit() == + Unit::Percent && + sizingModeCrossDim != SizingMode::StretchFit; + childCrossSizingMode = + yoga::isUndefined(childCrossSize) || isLoosePercentageMeasurement + ? SizingMode::MaxContent + : SizingMode::StretchFit; + } + + constrainMaxSizeForMode( + currentLineChild, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainSizingMode, + &childMainSize); + constrainMaxSizeForMode( + currentLineChild, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossSizingMode, + &childCrossSize); + + const bool requiresStretchLayout = + !currentLineChild->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim) && + resolveChildAlignment(node, currentLineChild) == Align::Stretch && + !currentLineChild->style().flexStartMarginIsAuto( + crossAxis, direction) && + !currentLineChild->style().flexEndMarginIsAuto(crossAxis, direction); + + const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; + const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; + + const SizingMode childWidthSizingMode = + isMainAxisRow ? childMainSizingMode : childCrossSizingMode; + const SizingMode childHeightSizingMode = + !isMainAxisRow ? childMainSizingMode : childCrossSizingMode; + + const bool isLayoutPass = performLayout && !requiresStretchLayout; + // Recursively call the layout algorithm for this child with the updated + // main size. + calculateLayoutInternal( + currentLineChild, + childWidth, + childHeight, + node->getLayout().direction(), + childWidthSizingMode, + childHeightSizingMode, + availableInnerWidth, + availableInnerHeight, + isLayoutPass, + isLayoutPass ? LayoutPassReason::kFlexLayout + : LayoutPassReason::kFlexMeasure, + layoutMarkerData, + depth, + generationCount); + node->setLayoutHadOverflow( + node->getLayout().hadOverflow() || + currentLineChild->getLayout().hadOverflow()); + } + return deltaFreeSpace; +} + +// It distributes the free space to the flexible items.For those flexible items +// whose min and max constraints are triggered, those flex item's clamped size +// is removed from the remaingfreespace. +static void distributeFreeSpaceFirstPass( + FlexLine& flexLine, + const FlexDirection mainAxis, + const float mainAxisownerSize, + const float availableInnerMainDim, + const float availableInnerWidth) { + float flexShrinkScaledFactor = 0; + float flexGrowFactor = 0; + float baseMainSize = 0; + float boundMainSize = 0; + float deltaFreeSpace = 0; + + for (auto currentLineChild : flexLine.itemsInFlow) { + float childFlexBasis = boundAxisWithinMinAndMax( + currentLineChild, + mainAxis, + currentLineChild->getLayout().computedFlexBasis, + mainAxisownerSize) + .unwrap(); + + if (flexLine.layout.remainingFreeSpace < 0) { + flexShrinkScaledFactor = + -currentLineChild->resolveFlexShrink() * childFlexBasis; + + // Is this child able to shrink? + if (yoga::isDefined(flexShrinkScaledFactor) && + flexShrinkScaledFactor != 0) { + baseMainSize = childFlexBasis + + flexLine.layout.remainingFreeSpace / + flexLine.layout.totalFlexShrinkScaledFactors * + flexShrinkScaledFactor; + boundMainSize = boundAxis( + currentLineChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + if (yoga::isDefined(baseMainSize) && yoga::isDefined(boundMainSize) && + baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this + // item's min/max constraints should also trigger in the second pass + // resulting in the item's size calculation being identical in the + // first and second passes. + deltaFreeSpace += boundMainSize - childFlexBasis; + flexLine.layout.totalFlexShrinkScaledFactors -= + (-currentLineChild->resolveFlexShrink() * + currentLineChild->getLayout().computedFlexBasis.unwrap()); + } + } + } else if ( + yoga::isDefined(flexLine.layout.remainingFreeSpace) && + flexLine.layout.remainingFreeSpace > 0) { + flexGrowFactor = currentLineChild->resolveFlexGrow(); + + // Is this child able to grow? + if (yoga::isDefined(flexGrowFactor) && flexGrowFactor != 0) { + baseMainSize = childFlexBasis + + flexLine.layout.remainingFreeSpace / + flexLine.layout.totalFlexGrowFactors * flexGrowFactor; + boundMainSize = boundAxis( + currentLineChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + + if (yoga::isDefined(baseMainSize) && yoga::isDefined(boundMainSize) && + baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this + // item's min/max constraints should also trigger in the second pass + // resulting in the item's size calculation being identical in the + // first and second passes. + deltaFreeSpace += boundMainSize - childFlexBasis; + flexLine.layout.totalFlexGrowFactors -= flexGrowFactor; + } + } + } + } + flexLine.layout.remainingFreeSpace -= deltaFreeSpace; +} + +// Do two passes over the flex items to figure out how to distribute the +// remaining space. +// +// The first pass finds the items whose min/max constraints trigger, freezes +// them at those sizes, and excludes those sizes from the remaining space. +// +// The second pass sets the size of each flexible item. It distributes the +// remaining space amongst the items whose min/max constraints didn't trigger in +// the first pass. For the other items, it sets their sizes by forcing their +// min/max constraints to trigger again. +// +// This two pass approach for resolving min/max constraints deviates from the +// spec. The spec +// (https://www.w3.org/TR/CSS-flexbox-1/#resolve-flexible-lengths) describes a +// process that needs to be repeated a variable number of times. The algorithm +// implemented here won't handle all cases but it was simpler to implement and +// it mitigates performance concerns because we know exactly how many passes +// it'll do. +// +// At the end of this function the child nodes would have the proper size +// assigned to them. +// +static void resolveFlexibleLength( + yoga::Node* const node, + FlexLine& flexLine, + const FlexDirection mainAxis, + const FlexDirection crossAxis, + const Direction direction, + const float mainAxisownerSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool mainAxisOverflows, + const SizingMode sizingModeCrossDim, + const bool performLayout, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + const float originalFreeSpace = flexLine.layout.remainingFreeSpace; + // First pass: detect the flex items whose min/max constraints trigger + distributeFreeSpaceFirstPass( + flexLine, + mainAxis, + mainAxisownerSize, + availableInnerMainDim, + availableInnerWidth); + + // Second pass: resolve the sizes of the flexible items + const float distributedFreeSpace = distributeFreeSpaceSecondPass( + flexLine, + node, + mainAxis, + crossAxis, + direction, + mainAxisownerSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + mainAxisOverflows, + sizingModeCrossDim, + performLayout, + layoutMarkerData, + depth, + generationCount); + + flexLine.layout.remainingFreeSpace = originalFreeSpace - distributedFreeSpace; +} + +static void justifyMainAxis( + yoga::Node* const node, + FlexLine& flexLine, + const size_t startOfLineIndex, + const FlexDirection mainAxis, + const FlexDirection crossAxis, + const Direction direction, + const SizingMode sizingModeMainDim, + const SizingMode sizingModeCrossDim, + const float mainAxisownerSize, + const float ownerWidth, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const bool performLayout) { + const auto& style = node->style(); + + const float leadingPaddingAndBorderMain = + node->style().computeFlexStartPaddingAndBorder( + mainAxis, direction, ownerWidth); + const float trailingPaddingAndBorderMain = + node->style().computeFlexEndPaddingAndBorder( + mainAxis, direction, ownerWidth); + + const float gap = + node->style().computeGapForAxis(mainAxis, availableInnerMainDim); + // If we are using "at most" rules in the main axis, make sure that + // remainingFreeSpace is 0 when min main dimension is not given + if (sizingModeMainDim == SizingMode::FitContent && + flexLine.layout.remainingFreeSpace > 0) { + if (style.minDimension(dimension(mainAxis)).isDefined() && + style.minDimension(dimension(mainAxis)) + .resolve(mainAxisownerSize) + .isDefined()) { + // This condition makes sure that if the size of main dimension(after + // considering child nodes main dim, leading and trailing padding etc) + // falls below min dimension, then the remainingFreeSpace is reassigned + // considering the min dimension + + // `minAvailableMainDim` denotes minimum available space in which child + // can be laid out, it will exclude space consumed by padding and border. + const float minAvailableMainDim = style.minDimension(dimension(mainAxis)) + .resolve(mainAxisownerSize) + .unwrap() - + leadingPaddingAndBorderMain - trailingPaddingAndBorderMain; + const float occupiedSpaceByChildNodes = + availableInnerMainDim - flexLine.layout.remainingFreeSpace; + flexLine.layout.remainingFreeSpace = yoga::maxOrDefined( + 0.0f, minAvailableMainDim - occupiedSpaceByChildNodes); + } else { + flexLine.layout.remainingFreeSpace = 0; + } + } + + // In order to position the elements in the main axis, we have two controls. + // The space between the beginning and the first element and the space between + // each two elements. + float leadingMainDim = 0; + float betweenMainDim = gap; + const Justify justifyContent = flexLine.layout.remainingFreeSpace >= 0 + ? node->style().justifyContent() + : fallbackAlignment(node->style().justifyContent()); + + if (flexLine.numberOfAutoMargins == 0) { + switch (justifyContent) { + case Justify::Center: + leadingMainDim = flexLine.layout.remainingFreeSpace / 2; + break; + case Justify::FlexEnd: + leadingMainDim = flexLine.layout.remainingFreeSpace; + break; + case Justify::SpaceBetween: + if (flexLine.itemsInFlow.size() > 1) { + betweenMainDim += flexLine.layout.remainingFreeSpace / + static_cast(flexLine.itemsInFlow.size() - 1); + } + break; + case Justify::SpaceEvenly: + // Space is distributed evenly across all elements + leadingMainDim = flexLine.layout.remainingFreeSpace / + static_cast(flexLine.itemsInFlow.size() + 1); + betweenMainDim += leadingMainDim; + break; + case Justify::SpaceAround: + // Space on the edges is half of the space between elements + leadingMainDim = 0.5f * flexLine.layout.remainingFreeSpace / + static_cast(flexLine.itemsInFlow.size()); + betweenMainDim += leadingMainDim * 2; + break; + case Justify::FlexStart: + break; + } + } + + flexLine.layout.mainDim = leadingPaddingAndBorderMain + leadingMainDim; + flexLine.layout.crossDim = 0; + + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; + bool isNodeBaselineLayout = isBaselineLayout(node); + for (size_t i = startOfLineIndex; i < flexLine.endOfLineIndex; i++) { + const auto child = node->getChild(i); + const Style& childStyle = child->style(); + const LayoutResults& childLayout = child->getLayout(); + if (childStyle.display() == Display::None) { + continue; + } + if (childStyle.positionType() == PositionType::Absolute && + child->style().isFlexStartPositionDefined(mainAxis, direction)) { + if (performLayout) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said (and + // margin/border). + child->setLayoutPosition( + child->style().computeFlexStartPosition( + mainAxis, direction, availableInnerMainDim) + + node->style().computeFlexStartBorder(mainAxis, direction) + + child->style().computeFlexStartMargin( + mainAxis, direction, availableInnerWidth), + flexStartEdge(mainAxis)); + } + } else { + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements do not + // take part in that phase. + if (childStyle.positionType() != PositionType::Absolute) { + if (child->style().flexStartMarginIsAuto(mainAxis, direction) && + flexLine.layout.remainingFreeSpace > 0.0f) { + flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace / + static_cast(flexLine.numberOfAutoMargins); + } + + if (performLayout) { + child->setLayoutPosition( + childLayout.position(flexStartEdge(mainAxis)) + + flexLine.layout.mainDim, + flexStartEdge(mainAxis)); + } + + if (child != flexLine.itemsInFlow.back()) { + flexLine.layout.mainDim += betweenMainDim; + } + + if (child->style().flexEndMarginIsAuto(mainAxis, direction) && + flexLine.layout.remainingFreeSpace > 0.0f) { + flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace / + static_cast(flexLine.numberOfAutoMargins); + } + bool canSkipFlex = + !performLayout && sizingModeCrossDim == SizingMode::StretchFit; + if (canSkipFlex) { + // If we skipped the flex step, then we can't rely on the measuredDims + // because they weren't computed. This means we can't call + // dimensionWithMargin. + flexLine.layout.mainDim += child->style().computeMarginForAxis( + mainAxis, availableInnerWidth) + + childLayout.computedFlexBasis.unwrap(); + flexLine.layout.crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + flexLine.layout.mainDim += + child->dimensionWithMargin(mainAxis, availableInnerWidth); + + if (isNodeBaselineLayout) { + // If the child is baseline aligned then the cross dimension is + // calculated by adding maxAscent and maxDescent from the baseline. + const float ascent = calculateBaseline(child) + + child->style().computeFlexStartMargin( + FlexDirection::Column, direction, availableInnerWidth); + const float descent = + child->getLayout().measuredDimension(Dimension::Height) + + child->style().computeMarginForAxis( + FlexDirection::Column, availableInnerWidth) - + ascent; + + maxAscentForCurrentLine = + yoga::maxOrDefined(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = + yoga::maxOrDefined(maxDescentForCurrentLine, descent); + } else { + // The cross dimension is the max of the elements dimension since + // there can only be one element in that cross dimension in the case + // when the items are not baseline aligned + flexLine.layout.crossDim = yoga::maxOrDefined( + flexLine.layout.crossDim, + child->dimensionWithMargin(crossAxis, availableInnerWidth)); + } + } + } else if (performLayout) { + child->setLayoutPosition( + childLayout.position(flexStartEdge(mainAxis)) + + node->style().computeFlexStartBorder(mainAxis, direction) + + leadingMainDim, + flexStartEdge(mainAxis)); + } + } + } + flexLine.layout.mainDim += trailingPaddingAndBorderMain; + + if (isNodeBaselineLayout) { + flexLine.layout.crossDim = + maxAscentForCurrentLine + maxDescentForCurrentLine; + } +} + +// +// This is the main routine that implements a subset of the flexbox layout +// algorithm described in the W3C CSS documentation: +// https://www.w3.org/TR/CSS3-flexbox/. +// +// Limitations of this algorithm, compared to the full standard: +// * Display property is always assumed to be 'flex' except for Text nodes, +// which are assumed to be 'inline-flex'. +// * The 'zIndex' property (or any form of z ordering) is not supported. Nodes +// are stacked in document order. +// * The 'order' property is not supported. The order of flex items is always +// defined by document order. +// * The 'visibility' property is always assumed to be 'visible'. Values of +// 'collapse' and 'hidden' are not supported. +// * There is no support for forced breaks. +// * It does not support vertical inline directions (top-to-bottom or +// bottom-to-top text). +// +// Deviations from standard: +// * Section 4.5 of the spec indicates that all flex items have a default +// minimum main size. For text blocks, for example, this is the width of the +// widest word. Calculating the minimum width is expensive, so we forego it +// and assume a default minimum main size of 0. +// * Min/Max sizes in the main axis are not honored when resolving flexible +// lengths. +// * The spec indicates that the default value for 'flexDirection' is 'row', +// but the algorithm below assumes a default of 'column'. +// +// Input parameters: +// - node: current node to be sized and laid out +// - availableWidth & availableHeight: available size to be used for sizing +// the node or YGUndefined if the size is not available; interpretation +// depends on layout flags +// - ownerDirection: the inline (text) direction within the owner +// (left-to-right or right-to-left) +// - widthSizingMode: indicates the sizing rules for the width (see below +// for explanation) +// - heightSizingMode: indicates the sizing rules for the height (see below +// for explanation) +// - performLayout: specifies whether the caller is interested in just the +// dimensions of the node or it requires the entire node and its subtree to +// be laid out (with final positions) +// +// Details: +// This routine is called recursively to lay out subtrees of flexbox +// elements. It uses the information in node.style, which is treated as a +// read-only input. It is responsible for setting the layout.direction and +// layout.measuredDimensions fields for the input node as well as the +// layout.position and layout.lineIndex fields for its child nodes. The +// layout.measuredDimensions field includes any border or padding for the +// node but does not include margins. +// +// When calling calculateLayoutImpl and calculateLayoutInternal, if the +// caller passes an available size of undefined then it must also pass a +// measure mode of SizingMode::MaxContent in that dimension. +// +static void calculateLayoutImpl( + yoga::Node* const node, + const float availableWidth, + const float availableHeight, + const Direction ownerDirection, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount, + const LayoutPassReason reason) { + yoga::assertFatalWithNode( + node, + yoga::isUndefined(availableWidth) + ? widthSizingMode == SizingMode::MaxContent + : true, + "availableWidth is indefinite so widthSizingMode must be " + "SizingMode::MaxContent"); + yoga::assertFatalWithNode( + node, + yoga::isUndefined(availableHeight) + ? heightSizingMode == SizingMode::MaxContent + : true, + "availableHeight is indefinite so heightSizingMode must be " + "SizingMode::MaxContent"); + + (performLayout ? layoutMarkerData.layouts : layoutMarkerData.measures) += 1; + + // Set the resolved resolution in the node's layout. + const Direction direction = node->resolveDirection(ownerDirection); + node->setLayoutDirection(direction); + + const FlexDirection flexRowDirection = + resolveDirection(FlexDirection::Row, direction); + const FlexDirection flexColumnDirection = + resolveDirection(FlexDirection::Column, direction); + + const auto startEdge = + direction == Direction::LTR ? PhysicalEdge::Left : PhysicalEdge::Right; + const auto endEdge = + direction == Direction::LTR ? PhysicalEdge::Right : PhysicalEdge::Left; + + const float marginRowLeading = node->style().computeInlineStartMargin( + flexRowDirection, direction, ownerWidth); + node->setLayoutMargin(marginRowLeading, startEdge); + const float marginRowTrailing = node->style().computeInlineEndMargin( + flexRowDirection, direction, ownerWidth); + node->setLayoutMargin(marginRowTrailing, endEdge); + const float marginColumnLeading = node->style().computeInlineStartMargin( + flexColumnDirection, direction, ownerWidth); + node->setLayoutMargin(marginColumnLeading, PhysicalEdge::Top); + const float marginColumnTrailing = node->style().computeInlineEndMargin( + flexColumnDirection, direction, ownerWidth); + node->setLayoutMargin(marginColumnTrailing, PhysicalEdge::Bottom); + + const float marginAxisRow = marginRowLeading + marginRowTrailing; + const float marginAxisColumn = marginColumnLeading + marginColumnTrailing; + + node->setLayoutBorder( + node->style().computeInlineStartBorder(flexRowDirection, direction), + startEdge); + node->setLayoutBorder( + node->style().computeInlineEndBorder(flexRowDirection, direction), + endEdge); + node->setLayoutBorder( + node->style().computeInlineStartBorder(flexColumnDirection, direction), + PhysicalEdge::Top); + node->setLayoutBorder( + node->style().computeInlineEndBorder(flexColumnDirection, direction), + PhysicalEdge::Bottom); + + node->setLayoutPadding( + node->style().computeInlineStartPadding( + flexRowDirection, direction, ownerWidth), + startEdge); + node->setLayoutPadding( + node->style().computeInlineEndPadding( + flexRowDirection, direction, ownerWidth), + endEdge); + node->setLayoutPadding( + node->style().computeInlineStartPadding( + flexColumnDirection, direction, ownerWidth), + PhysicalEdge::Top); + node->setLayoutPadding( + node->style().computeInlineEndPadding( + flexColumnDirection, direction, ownerWidth), + PhysicalEdge::Bottom); + + if (node->hasMeasureFunc()) { + measureNodeWithMeasureFunc( + node, + availableWidth - marginAxisRow, + availableHeight - marginAxisColumn, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight, + layoutMarkerData, + reason); + return; + } + + const auto childCount = node->getChildCount(); + if (childCount == 0) { + measureNodeWithoutChildren( + node, + availableWidth - marginAxisRow, + availableHeight - marginAxisColumn, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight); + return; + } + + // If we're not being asked to perform a full layout we can skip the algorithm + // if we already know the size + if (!performLayout && + measureNodeWithFixedSize( + node, + availableWidth - marginAxisRow, + availableHeight - marginAxisColumn, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight)) { + return; + } + + // At this point we know we're going to perform work. Ensure that each child + // has a mutable copy. + node->cloneChildrenIfNeeded(); + // Reset layout flags, as they could have changed. + node->setLayoutHadOverflow(false); + + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + const FlexDirection mainAxis = + resolveDirection(node->style().flexDirection(), direction); + const FlexDirection crossAxis = resolveCrossDirection(mainAxis, direction); + const bool isMainAxisRow = isRow(mainAxis); + const bool isNodeFlexWrap = node->style().flexWrap() != Wrap::NoWrap; + + const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight; + const float crossAxisownerSize = isMainAxisRow ? ownerHeight : ownerWidth; + + const float paddingAndBorderAxisMain = + paddingAndBorderForAxis(node, mainAxis, ownerWidth); + const float paddingAndBorderAxisCross = + paddingAndBorderForAxis(node, crossAxis, ownerWidth); + const float leadingPaddingAndBorderCross = + node->style().computeFlexStartPaddingAndBorder( + crossAxis, direction, ownerWidth); + + SizingMode sizingModeMainDim = + isMainAxisRow ? widthSizingMode : heightSizingMode; + SizingMode sizingModeCrossDim = + isMainAxisRow ? heightSizingMode : widthSizingMode; + + const float paddingAndBorderAxisRow = + isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross; + const float paddingAndBorderAxisColumn = + isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain; + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + + float availableInnerWidth = calculateAvailableInnerDimension( + node, + Dimension::Width, + availableWidth - marginAxisRow, + paddingAndBorderAxisRow, + ownerWidth); + float availableInnerHeight = calculateAvailableInnerDimension( + node, + Dimension::Height, + availableHeight - marginAxisColumn, + paddingAndBorderAxisColumn, + ownerHeight); + + float availableInnerMainDim = + isMainAxisRow ? availableInnerWidth : availableInnerHeight; + const float availableInnerCrossDim = + isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + + // Computed basis + margins + gap + float totalMainDim = 0; + totalMainDim += computeFlexBasisForChildren( + node, + availableInnerWidth, + availableInnerHeight, + widthSizingMode, + heightSizingMode, + direction, + mainAxis, + performLayout, + layoutMarkerData, + depth, + generationCount); + + if (childCount > 1) { + totalMainDim += + node->style().computeGapForAxis(mainAxis, availableInnerMainDim) * + static_cast(childCount - 1); + } + + const bool mainAxisOverflows = + (sizingModeMainDim != SizingMode::MaxContent) && + totalMainDim > availableInnerMainDim; + + if (isNodeFlexWrap && mainAxisOverflows && + sizingModeMainDim == SizingMode::FitContent) { + sizingModeMainDim = SizingMode::StretchFit; + } + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + size_t startOfLineIndex = 0; + size_t endOfLineIndex = 0; + + // Number of lines. + size_t lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + float totalLineCrossDim = 0; + + const float crossAxisGap = + node->style().computeGapForAxis(crossAxis, availableInnerCrossDim); + + // Max main dimension of all the lines. + float maxLineMainDim = 0; + for (; endOfLineIndex < childCount; + lineCount++, startOfLineIndex = endOfLineIndex) { + auto flexLine = calculateFlexLine( + node, + ownerDirection, + mainAxisownerSize, + availableInnerWidth, + availableInnerMainDim, + startOfLineIndex, + lineCount); + + endOfLineIndex = flexLine.endOfLineIndex; + + // If we don't need to measure the cross axis, we can skip the entire flex + // step. + const bool canSkipFlex = + !performLayout && sizingModeCrossDim == SizingMode::StretchFit; + + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. If + // the main dimension size isn't known, it is computed based on the line + // length, so there's no more space left to distribute. + + bool sizeBasedOnContent = false; + // If we don't measure with exact main dimension we want to ensure we don't + // violate min and max + if (sizingModeMainDim != SizingMode::StretchFit) { + const auto& style = node->style(); + const float minInnerWidth = + style.minDimension(Dimension::Width).resolve(ownerWidth).unwrap() - + paddingAndBorderAxisRow; + const float maxInnerWidth = + style.maxDimension(Dimension::Width).resolve(ownerWidth).unwrap() - + paddingAndBorderAxisRow; + const float minInnerHeight = + style.minDimension(Dimension::Height).resolve(ownerHeight).unwrap() - + paddingAndBorderAxisColumn; + const float maxInnerHeight = + style.maxDimension(Dimension::Height).resolve(ownerHeight).unwrap() - + paddingAndBorderAxisColumn; + + const float minInnerMainDim = + isMainAxisRow ? minInnerWidth : minInnerHeight; + const float maxInnerMainDim = + isMainAxisRow ? maxInnerWidth : maxInnerHeight; + + if (yoga::isDefined(minInnerMainDim) && + flexLine.sizeConsumed < minInnerMainDim) { + availableInnerMainDim = minInnerMainDim; + } else if ( + yoga::isDefined(maxInnerMainDim) && + flexLine.sizeConsumed > maxInnerMainDim) { + availableInnerMainDim = maxInnerMainDim; + } else { + bool useLegacyStretchBehaviour = + node->hasErrata(Errata::StretchFlexBasis); + + if (!useLegacyStretchBehaviour && + ((yoga::isDefined(flexLine.layout.totalFlexGrowFactors) && + flexLine.layout.totalFlexGrowFactors == 0) || + (yoga::isDefined(node->resolveFlexGrow()) && + node->resolveFlexGrow() == 0))) { + // If we don't have any children to flex or we can't flex the node + // itself, space we've used is all space we need. Root node also + // should be shrunk to minimum + availableInnerMainDim = flexLine.sizeConsumed; + } + + sizeBasedOnContent = !useLegacyStretchBehaviour; + } + } + + if (!sizeBasedOnContent && yoga::isDefined(availableInnerMainDim)) { + flexLine.layout.remainingFreeSpace = + availableInnerMainDim - flexLine.sizeConsumed; + } else if (flexLine.sizeConsumed < 0) { + // availableInnerMainDim is indefinite which means the node is being sized + // based on its content. sizeConsumed is negative which means + // the node will allocate 0 points for its content. Consequently, + // remainingFreeSpace is 0 - sizeConsumed. + flexLine.layout.remainingFreeSpace = -flexLine.sizeConsumed; + } + + if (!canSkipFlex) { + resolveFlexibleLength( + node, + flexLine, + mainAxis, + crossAxis, + direction, + mainAxisownerSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + mainAxisOverflows, + sizingModeCrossDim, + performLayout, + layoutMarkerData, + depth, + generationCount); + } + + node->setLayoutHadOverflow( + node->getLayout().hadOverflow() || + (flexLine.layout.remainingFreeSpace < 0)); + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main + // axis. Their dimensions are also set in the cross axis with the exception + // of items that are aligned "stretch". We need to compute these stretch + // values and set the final positions. + + justifyMainAxis( + node, + flexLine, + startOfLineIndex, + mainAxis, + crossAxis, + direction, + sizingModeMainDim, + sizingModeCrossDim, + mainAxisownerSize, + ownerWidth, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + performLayout); + + float containerCrossAxis = availableInnerCrossDim; + if (sizingModeCrossDim == SizingMode::MaxContent || + sizingModeCrossDim == SizingMode::FitContent) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = + boundAxis( + node, + crossAxis, + flexLine.layout.crossDim + paddingAndBorderAxisCross, + crossAxisownerSize, + ownerWidth) - + paddingAndBorderAxisCross; + } + + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && sizingModeCrossDim == SizingMode::StretchFit) { + flexLine.layout.crossDim = availableInnerCrossDim; + } + + // As-per https://www.w3.org/TR/css-flexbox-1/#cross-sizing, the + // cross-size of the line within a single-line container should be bound to + // min/max constraints before alignment within the line. In a multi-line + // container, affecting alignment between the lines. + if (!isNodeFlexWrap) { + flexLine.layout.crossDim = + boundAxis( + node, + crossAxis, + flexLine.layout.crossDim + paddingAndBorderAxisCross, + crossAxisownerSize, + ownerWidth) - + paddingAndBorderAxisCross; + } + + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (size_t i = startOfLineIndex; i < endOfLineIndex; i++) { + const auto child = node->getChild(i); + if (child->style().display() == Display::None) { + continue; + } + if (child->style().positionType() == PositionType::Absolute) { + // If the child is absolutely positioned and has a + // top/left/bottom/right set, override all the previously computed + // positions to set it correctly. + const bool isChildLeadingPosDefined = + child->style().isFlexStartPositionDefined(crossAxis, direction); + if (isChildLeadingPosDefined) { + child->setLayoutPosition( + child->style().computeFlexStartPosition( + crossAxis, direction, availableInnerCrossDim) + + node->style().computeFlexStartBorder(crossAxis, direction) + + child->style().computeFlexStartMargin( + crossAxis, direction, availableInnerWidth), + flexStartEdge(crossAxis)); + } + // If leading position is not defined or calculations result in Nan, + // default to border + margin + if (!isChildLeadingPosDefined || + yoga::isUndefined( + child->getLayout().position(flexStartEdge(crossAxis)))) { + child->setLayoutPosition( + node->style().computeFlexStartBorder(crossAxis, direction) + + child->style().computeFlexStartMargin( + crossAxis, direction, availableInnerWidth), + flexStartEdge(crossAxis)); + } + } else { + float leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (owner) or + // alignSelf (child) in order to determine the position in the cross + // axis + const Align alignItem = resolveChildAlignment(node, child); + + // If the child uses align stretch, we need to lay it out one more + // time, this time forcing the cross-axis size to be the computed + // cross size for the current line. + if (alignItem == Align::Stretch && + !child->style().flexStartMarginIsAuto(crossAxis, direction) && + !child->style().flexEndMarginIsAuto(crossAxis, direction)) { + // If the child defines a definite size for its cross axis, there's + // no need to stretch. + if (!child->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim)) { + float childMainSize = + child->getLayout().measuredDimension(dimension(mainAxis)); + const auto& childStyle = child->style(); + float childCrossSize = childStyle.aspectRatio().isDefined() + ? child->style().computeMarginForAxis( + crossAxis, availableInnerWidth) + + (isMainAxisRow + ? childMainSize / childStyle.aspectRatio().unwrap() + : childMainSize * childStyle.aspectRatio().unwrap()) + : flexLine.layout.crossDim; + + childMainSize += child->style().computeMarginForAxis( + mainAxis, availableInnerWidth); + + SizingMode childMainSizingMode = SizingMode::StretchFit; + SizingMode childCrossSizingMode = SizingMode::StretchFit; + constrainMaxSizeForMode( + child, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainSizingMode, + &childMainSize); + constrainMaxSizeForMode( + child, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossSizingMode, + &childCrossSize); + + const float childWidth = + isMainAxisRow ? childMainSize : childCrossSize; + const float childHeight = + !isMainAxisRow ? childMainSize : childCrossSize; + + auto alignContent = node->style().alignContent(); + auto crossAxisDoesNotGrow = + alignContent != Align::Stretch && isNodeFlexWrap; + const SizingMode childWidthSizingMode = + yoga::isUndefined(childWidth) || + (!isMainAxisRow && crossAxisDoesNotGrow) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + const SizingMode childHeightSizingMode = + yoga::isUndefined(childHeight) || + (isMainAxisRow && crossAxisDoesNotGrow) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + childWidthSizingMode, + childHeightSizingMode, + availableInnerWidth, + availableInnerHeight, + true, + LayoutPassReason::kStretch, + layoutMarkerData, + depth, + generationCount); + } + } else { + const float remainingCrossDim = containerCrossAxis - + child->dimensionWithMargin(crossAxis, availableInnerWidth); + + if (child->style().flexStartMarginIsAuto(crossAxis, direction) && + child->style().flexEndMarginIsAuto(crossAxis, direction)) { + leadingCrossDim += + yoga::maxOrDefined(0.0f, remainingCrossDim / 2); + } else if (child->style().flexEndMarginIsAuto( + crossAxis, direction)) { + // No-Op + } else if (child->style().flexStartMarginIsAuto( + crossAxis, direction)) { + leadingCrossDim += yoga::maxOrDefined(0.0f, remainingCrossDim); + } else if (alignItem == Align::FlexStart) { + // No-Op + } else if (alignItem == Align::Center) { + leadingCrossDim += remainingCrossDim / 2; + } else { + leadingCrossDim += remainingCrossDim; + } + } + // And we apply the position + child->setLayoutPosition( + child->getLayout().position(flexStartEdge(crossAxis)) + + totalLineCrossDim + leadingCrossDim, + flexStartEdge(crossAxis)); + } + } + } + + const float appliedCrossGap = lineCount != 0 ? crossAxisGap : 0.0f; + totalLineCrossDim += flexLine.layout.crossDim + appliedCrossGap; + maxLineMainDim = + yoga::maxOrDefined(maxLineMainDim, flexLine.layout.mainDim); + } + + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + // currentLead stores the size of the cross dim + if (performLayout && (isNodeFlexWrap || isBaselineLayout(node))) { + float leadPerLine = 0; + float currentLead = leadingPaddingAndBorderCross; + + const float unclampedCrossDim = sizingModeCrossDim == SizingMode::StretchFit + ? availableInnerCrossDim + paddingAndBorderAxisCross + : node->hasDefiniteLength(dimension(crossAxis), crossAxisownerSize) + ? node->getResolvedDimension(dimension(crossAxis)) + .resolve(crossAxisownerSize) + .unwrap() + : totalLineCrossDim + paddingAndBorderAxisCross; + + const float innerCrossDim = + boundAxis(node, crossAxis, unclampedCrossDim, ownerHeight, ownerWidth) - + paddingAndBorderAxisCross; + + const float remainingAlignContentDim = innerCrossDim - totalLineCrossDim; + + const auto alignContent = remainingAlignContentDim >= 0 + ? node->style().alignContent() + : fallbackAlignment(node->style().alignContent()); + + switch (alignContent) { + case Align::FlexEnd: + currentLead += remainingAlignContentDim; + break; + case Align::Center: + currentLead += remainingAlignContentDim / 2; + break; + case Align::Stretch: + leadPerLine = remainingAlignContentDim / static_cast(lineCount); + break; + case Align::SpaceAround: + currentLead += + remainingAlignContentDim / (2 * static_cast(lineCount)); + leadPerLine = remainingAlignContentDim / static_cast(lineCount); + break; + case Align::SpaceEvenly: + currentLead += + remainingAlignContentDim / static_cast(lineCount + 1); + leadPerLine = + remainingAlignContentDim / static_cast(lineCount + 1); + break; + case Align::SpaceBetween: + if (lineCount > 1) { + leadPerLine = + remainingAlignContentDim / static_cast(lineCount - 1); + } + break; + case Align::Auto: + case Align::FlexStart: + case Align::Baseline: + break; + } + size_t endIndex = 0; + for (size_t i = 0; i < lineCount; i++) { + const size_t startIndex = endIndex; + size_t ii = startIndex; + + // compute the line's height and find the endIndex + float lineHeight = 0; + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; + for (; ii < childCount; ii++) { + const auto child = node->getChild(ii); + if (child->style().display() == Display::None) { + continue; + } + if (child->style().positionType() != PositionType::Absolute) { + if (child->getLineIndex() != i) { + break; + } + if (child->isLayoutDimensionDefined(crossAxis)) { + lineHeight = yoga::maxOrDefined( + lineHeight, + child->getLayout().measuredDimension(dimension(crossAxis)) + + child->style().computeMarginForAxis( + crossAxis, availableInnerWidth)); + } + if (resolveChildAlignment(node, child) == Align::Baseline) { + const float ascent = calculateBaseline(child) + + child->style().computeFlexStartMargin( + FlexDirection::Column, direction, availableInnerWidth); + const float descent = + child->getLayout().measuredDimension(Dimension::Height) + + child->style().computeMarginForAxis( + FlexDirection::Column, availableInnerWidth) - + ascent; + maxAscentForCurrentLine = + yoga::maxOrDefined(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = + yoga::maxOrDefined(maxDescentForCurrentLine, descent); + lineHeight = yoga::maxOrDefined( + lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); + } + } + } + endIndex = ii; + currentLead += i != 0 ? crossAxisGap : 0; + + for (ii = startIndex; ii < endIndex; ii++) { + const auto child = node->getChild(ii); + if (child->style().display() == Display::None) { + continue; + } + if (child->style().positionType() != PositionType::Absolute) { + switch (resolveChildAlignment(node, child)) { + case Align::FlexStart: { + child->setLayoutPosition( + currentLead + + child->style().computeFlexStartPosition( + crossAxis, direction, availableInnerWidth), + flexStartEdge(crossAxis)); + break; + } + case Align::FlexEnd: { + child->setLayoutPosition( + currentLead + lineHeight - + child->style().computeFlexEndMargin( + crossAxis, direction, availableInnerWidth) - + child->getLayout().measuredDimension( + dimension(crossAxis)), + flexStartEdge(crossAxis)); + break; + } + case Align::Center: { + float childHeight = + child->getLayout().measuredDimension(dimension(crossAxis)); + + child->setLayoutPosition( + currentLead + (lineHeight - childHeight) / 2, + flexStartEdge(crossAxis)); + break; + } + case Align::Stretch: { + child->setLayoutPosition( + currentLead + + child->style().computeFlexStartMargin( + crossAxis, direction, availableInnerWidth), + flexStartEdge(crossAxis)); + + // Remeasure child with the line height as it as been only + // measured with the owners height yet. + if (!child->hasDefiniteLength( + dimension(crossAxis), availableInnerCrossDim)) { + const float childWidth = isMainAxisRow + ? (child->getLayout().measuredDimension(Dimension::Width) + + child->style().computeMarginForAxis( + mainAxis, availableInnerWidth)) + : leadPerLine + lineHeight; + + const float childHeight = !isMainAxisRow + ? (child->getLayout().measuredDimension(Dimension::Height) + + child->style().computeMarginForAxis( + crossAxis, availableInnerWidth)) + : leadPerLine + lineHeight; + + if (!(yoga::inexactEquals( + childWidth, + child->getLayout().measuredDimension( + Dimension::Width)) && + yoga::inexactEquals( + childHeight, + child->getLayout().measuredDimension( + Dimension::Height)))) { + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + SizingMode::StretchFit, + SizingMode::StretchFit, + availableInnerWidth, + availableInnerHeight, + true, + LayoutPassReason::kMultilineStretch, + layoutMarkerData, + depth, + generationCount); + } + } + break; + } + case Align::Baseline: { + child->setLayoutPosition( + currentLead + maxAscentForCurrentLine - + calculateBaseline(child) + + child->style().computeFlexStartPosition( + FlexDirection::Column, + direction, + availableInnerCrossDim), + PhysicalEdge::Top); + + break; + } + case Align::Auto: + case Align::SpaceBetween: + case Align::SpaceAround: + case Align::SpaceEvenly: + break; + } + } + } + + currentLead = currentLead + leadPerLine + lineHeight; + } + } + + // STEP 9: COMPUTING FINAL DIMENSIONS + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Row, + availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + Dimension::Width); + + node->setLayoutMeasuredDimension( + boundAxis( + node, + FlexDirection::Column, + availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + Dimension::Height); + + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (sizingModeMainDim == SizingMode::MaxContent || + (node->style().overflow() != Overflow::Scroll && + sizingModeMainDim == SizingMode::FitContent)) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->setLayoutMeasuredDimension( + boundAxis( + node, mainAxis, maxLineMainDim, mainAxisownerSize, ownerWidth), + dimension(mainAxis)); + + } else if ( + sizingModeMainDim == SizingMode::FitContent && + node->style().overflow() == Overflow::Scroll) { + node->setLayoutMeasuredDimension( + yoga::maxOrDefined( + yoga::minOrDefined( + availableInnerMainDim + paddingAndBorderAxisMain, + boundAxisWithinMinAndMax( + node, + mainAxis, + FloatOptional{maxLineMainDim}, + mainAxisownerSize) + .unwrap()), + paddingAndBorderAxisMain), + dimension(mainAxis)); + } + + if (sizingModeCrossDim == SizingMode::MaxContent || + (node->style().overflow() != Overflow::Scroll && + sizingModeCrossDim == SizingMode::FitContent)) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->setLayoutMeasuredDimension( + boundAxis( + node, + crossAxis, + totalLineCrossDim + paddingAndBorderAxisCross, + crossAxisownerSize, + ownerWidth), + dimension(crossAxis)); + + } else if ( + sizingModeCrossDim == SizingMode::FitContent && + node->style().overflow() == Overflow::Scroll) { + node->setLayoutMeasuredDimension( + yoga::maxOrDefined( + yoga::minOrDefined( + availableInnerCrossDim + paddingAndBorderAxisCross, + boundAxisWithinMinAndMax( + node, + crossAxis, + FloatOptional{ + totalLineCrossDim + paddingAndBorderAxisCross}, + crossAxisownerSize) + .unwrap()), + paddingAndBorderAxisCross), + dimension(crossAxis)); + } + + // As we only wrapped in normal direction yet, we need to reverse the + // positions on wrap-reverse. + if (performLayout && node->style().flexWrap() == Wrap::WrapReverse) { + for (size_t i = 0; i < childCount; i++) { + const auto child = node->getChild(i); + if (child->style().positionType() != PositionType::Absolute) { + child->setLayoutPosition( + node->getLayout().measuredDimension(dimension(crossAxis)) - + child->getLayout().position(flexStartEdge(crossAxis)) - + child->getLayout().measuredDimension(dimension(crossAxis)), + flexStartEdge(crossAxis)); + } + } + } + + if (performLayout) { + // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN + const bool needsMainTrailingPos = needsTrailingPosition(mainAxis); + const bool needsCrossTrailingPos = needsTrailingPosition(crossAxis); + + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (size_t i = 0; i < childCount; i++) { + const auto child = node->getChild(i); + // Absolute children will be handled by their containing block since we + // cannot guarantee that their positions are set when their parents are + // done with layout. + if (child->style().display() == Display::None || + child->style().positionType() == PositionType::Absolute) { + continue; + } + if (needsMainTrailingPos) { + setChildTrailingPosition(node, child, mainAxis); + } + + if (needsCrossTrailingPos) { + setChildTrailingPosition(node, child, crossAxis); + } + } + } + + // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN + // Let the containing block layout its absolute descendants. + if (node->style().positionType() != PositionType::Static || + node->alwaysFormsContainingBlock() || depth == 1) { + layoutAbsoluteDescendants( + node, + node, + isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, + direction, + layoutMarkerData, + depth, + generationCount, + 0.0f, + 0.0f, + availableInnerWidth, + availableInnerHeight); + } + } +} + +// +// This is a wrapper around the calculateLayoutImpl function. It determines +// whether the layout request is redundant and can be skipped. +// +// Parameters: +// Input parameters are the same as calculateLayoutImpl (see above) +// Return parameter is true if layout was performed, false if skipped +// +bool calculateLayoutInternal( + yoga::Node* const node, + const float availableWidth, + const float availableHeight, + const Direction ownerDirection, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + const LayoutPassReason reason, + LayoutData& layoutMarkerData, + uint32_t depth, + const uint32_t generationCount) { + LayoutResults* layout = &node->getLayout(); + + depth++; + + const bool needToVisitNode = + (node->isDirty() && layout->generationCount != generationCount) || + layout->lastOwnerDirection != ownerDirection; + + if (needToVisitNode) { + // Invalidate the cached results. + layout->nextCachedMeasurementsIndex = 0; + layout->cachedLayout.availableWidth = -1; + layout->cachedLayout.availableHeight = -1; + layout->cachedLayout.widthSizingMode = SizingMode::MaxContent; + layout->cachedLayout.heightSizingMode = SizingMode::MaxContent; + layout->cachedLayout.computedWidth = -1; + layout->cachedLayout.computedHeight = -1; + } + + CachedMeasurement* cachedResults = nullptr; + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the + // positions and dimensions for nodes in the subtree. The algorithm assumes + // that each node gets laid out a maximum of one time per tree layout, but + // multiple measurements may be required to resolve all of the flex + // dimensions. We handle nodes with measure functions specially here because + // they are the most expensive to measure, so it's worth avoiding redundant + // measurements if at all possible. + if (node->hasMeasureFunc()) { + const float marginAxisRow = + node->style().computeMarginForAxis(FlexDirection::Row, ownerWidth); + const float marginAxisColumn = + node->style().computeMarginForAxis(FlexDirection::Column, ownerWidth); + + // First, try to use the layout cache. + if (canUseCachedMeasurement( + widthSizingMode, + availableWidth, + heightSizingMode, + availableHeight, + layout->cachedLayout.widthSizingMode, + layout->cachedLayout.availableWidth, + layout->cachedLayout.heightSizingMode, + layout->cachedLayout.availableHeight, + layout->cachedLayout.computedWidth, + layout->cachedLayout.computedHeight, + marginAxisRow, + marginAxisColumn, + node->getConfig())) { + cachedResults = &layout->cachedLayout; + } else { + // Try to use the measurement cache. + for (size_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (canUseCachedMeasurement( + widthSizingMode, + availableWidth, + heightSizingMode, + availableHeight, + layout->cachedMeasurements[i].widthSizingMode, + layout->cachedMeasurements[i].availableWidth, + layout->cachedMeasurements[i].heightSizingMode, + layout->cachedMeasurements[i].availableHeight, + layout->cachedMeasurements[i].computedWidth, + layout->cachedMeasurements[i].computedHeight, + marginAxisRow, + marginAxisColumn, + node->getConfig())) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + } else if (performLayout) { + if (yoga::inexactEquals( + layout->cachedLayout.availableWidth, availableWidth) && + yoga::inexactEquals( + layout->cachedLayout.availableHeight, availableHeight) && + layout->cachedLayout.widthSizingMode == widthSizingMode && + layout->cachedLayout.heightSizingMode == heightSizingMode) { + cachedResults = &layout->cachedLayout; + } + } else { + for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (yoga::inexactEquals( + layout->cachedMeasurements[i].availableWidth, availableWidth) && + yoga::inexactEquals( + layout->cachedMeasurements[i].availableHeight, availableHeight) && + layout->cachedMeasurements[i].widthSizingMode == widthSizingMode && + layout->cachedMeasurements[i].heightSizingMode == heightSizingMode) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults != nullptr) { + layout->setMeasuredDimension( + Dimension::Width, cachedResults->computedWidth); + layout->setMeasuredDimension( + Dimension::Height, cachedResults->computedHeight); + + (performLayout ? layoutMarkerData.cachedLayouts + : layoutMarkerData.cachedMeasures) += 1; + } else { + calculateLayoutImpl( + node, + availableWidth, + availableHeight, + ownerDirection, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight, + performLayout, + layoutMarkerData, + depth, + generationCount, + reason); + + layout->lastOwnerDirection = ownerDirection; + + if (cachedResults == nullptr) { + layoutMarkerData.maxMeasureCache = std::max( + layoutMarkerData.maxMeasureCache, + layout->nextCachedMeasurementsIndex + 1u); + + if (layout->nextCachedMeasurementsIndex == + LayoutResults::MaxCachedMeasurements) { + layout->nextCachedMeasurementsIndex = 0; + } + + CachedMeasurement* newCacheEntry = nullptr; + if (performLayout) { + // Use the single layout cache entry. + newCacheEntry = &layout->cachedLayout; + } else { + // Allocate a new measurement cache entry. + newCacheEntry = + &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex]; + layout->nextCachedMeasurementsIndex++; + } + + newCacheEntry->availableWidth = availableWidth; + newCacheEntry->availableHeight = availableHeight; + newCacheEntry->widthSizingMode = widthSizingMode; + newCacheEntry->heightSizingMode = heightSizingMode; + newCacheEntry->computedWidth = + layout->measuredDimension(Dimension::Width); + newCacheEntry->computedHeight = + layout->measuredDimension(Dimension::Height); + } + } + + if (performLayout) { + node->setLayoutDimension( + node->getLayout().measuredDimension(Dimension::Width), + Dimension::Width); + node->setLayoutDimension( + node->getLayout().measuredDimension(Dimension::Height), + Dimension::Height); + + node->setHasNewLayout(true); + node->setDirty(false); + } + + layout->generationCount = generationCount; + + LayoutType layoutType; + if (performLayout) { + layoutType = !needToVisitNode && cachedResults == &layout->cachedLayout + ? LayoutType::kCachedLayout + : LayoutType::kLayout; + } else { + layoutType = cachedResults != nullptr ? LayoutType::kCachedMeasure + : LayoutType::kMeasure; + } + Event::publish(node, {layoutType}); + + return (needToVisitNode || cachedResults == nullptr); +} + +void calculateLayout( + yoga::Node* const node, + const float ownerWidth, + const float ownerHeight, + const Direction ownerDirection) { + Event::publish(node); + LayoutData markerData = {}; + + // Increment the generation count. This will force the recursive routine to + // visit all dirty nodes at least once. Subsequent visits will be skipped if + // the input parameters don't change. + gCurrentGenerationCount.fetch_add(1, std::memory_order_relaxed); + node->resolveDimension(); + float width = YGUndefined; + SizingMode widthSizingMode = SizingMode::MaxContent; + const auto& style = node->style(); + if (node->hasDefiniteLength(Dimension::Width, ownerWidth)) { + width = + (node->getResolvedDimension(dimension(FlexDirection::Row)) + .resolve(ownerWidth) + .unwrap() + + node->style().computeMarginForAxis(FlexDirection::Row, ownerWidth)); + widthSizingMode = SizingMode::StretchFit; + } else if (style.maxDimension(Dimension::Width) + .resolve(ownerWidth) + .isDefined()) { + width = style.maxDimension(Dimension::Width).resolve(ownerWidth).unwrap(); + widthSizingMode = SizingMode::FitContent; + } else { + width = ownerWidth; + widthSizingMode = yoga::isUndefined(width) ? SizingMode::MaxContent + : SizingMode::StretchFit; + } + + float height = YGUndefined; + SizingMode heightSizingMode = SizingMode::MaxContent; + if (node->hasDefiniteLength(Dimension::Height, ownerHeight)) { + height = + (node->getResolvedDimension(dimension(FlexDirection::Column)) + .resolve(ownerHeight) + .unwrap() + + node->style().computeMarginForAxis(FlexDirection::Column, ownerWidth)); + heightSizingMode = SizingMode::StretchFit; + } else if (style.maxDimension(Dimension::Height) + .resolve(ownerHeight) + .isDefined()) { + height = + style.maxDimension(Dimension::Height).resolve(ownerHeight).unwrap(); + heightSizingMode = SizingMode::FitContent; + } else { + height = ownerHeight; + heightSizingMode = yoga::isUndefined(height) ? SizingMode::MaxContent + : SizingMode::StretchFit; + } + if (calculateLayoutInternal( + node, + width, + height, + ownerDirection, + widthSizingMode, + heightSizingMode, + ownerWidth, + ownerHeight, + true, + LayoutPassReason::kInitial, + markerData, + 0, // tree root + gCurrentGenerationCount.load(std::memory_order_relaxed))) { + node->setPosition( + node->getLayout().direction(), ownerWidth, ownerHeight, ownerWidth); + roundLayoutResultsToPixelGrid(node, 0.0f, 0.0f); + } + + Event::publish(node, {&markerData}); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/CalculateLayout.h b/Source/3rdParty/yoga/algorithm/CalculateLayout.h new file mode 100644 index 000000000..8c2a78465 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/CalculateLayout.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/Yoga.h" +#include "yoga/algorithm/FlexDirection.h" +#include "yoga/event/event.h" +#include "yoga/node/Node.h" + +namespace facebook::yoga { + +void calculateLayout( + yoga::Node* node, + float ownerWidth, + float ownerHeight, + Direction ownerDirection); + +bool calculateLayoutInternal( + yoga::Node* node, + float availableWidth, + float availableHeight, + Direction ownerDirection, + SizingMode widthSizingMode, + SizingMode heightSizingMode, + float ownerWidth, + float ownerHeight, + bool performLayout, + LayoutPassReason reason, + LayoutData& layoutMarkerData, + uint32_t depth, + uint32_t generationCount); + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/FlexDirection.h b/Source/3rdParty/yoga/algorithm/FlexDirection.h new file mode 100644 index 000000000..6a81246ee --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/FlexDirection.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/Yoga.h" + +#include "yoga/debug/AssertFatal.h" +#include "yoga/enums/Dimension.h" +#include "yoga/enums/Direction.h" +#include "yoga/enums/Edge.h" +#include "yoga/enums/FlexDirection.h" +#include "yoga/enums/PhysicalEdge.h" + +namespace facebook::yoga { + +inline bool isRow(const FlexDirection flexDirection) { + return flexDirection == FlexDirection::Row || + flexDirection == FlexDirection::RowReverse; +} + +inline bool isColumn(const FlexDirection flexDirection) { + return flexDirection == FlexDirection::Column || + flexDirection == FlexDirection::ColumnReverse; +} + +inline FlexDirection resolveDirection( + const FlexDirection flexDirection, + const Direction direction) { + if (direction == Direction::RTL) { + if (flexDirection == FlexDirection::Row) { + return FlexDirection::RowReverse; + } else if (flexDirection == FlexDirection::RowReverse) { + return FlexDirection::Row; + } + } + + return flexDirection; +} + +inline FlexDirection resolveCrossDirection( + const FlexDirection flexDirection, + const Direction direction) { + return isColumn(flexDirection) + ? resolveDirection(FlexDirection::Row, direction) + : FlexDirection::Column; +} + +inline PhysicalEdge flexStartEdge(FlexDirection flexDirection) { + switch (flexDirection) { + case FlexDirection::Column: + return PhysicalEdge::Top; + case FlexDirection::ColumnReverse: + return PhysicalEdge::Bottom; + case FlexDirection::Row: + return PhysicalEdge::Left; + case FlexDirection::RowReverse: + return PhysicalEdge::Right; + } + + fatalWithMessage("Invalid FlexDirection"); +} + +inline PhysicalEdge flexEndEdge(FlexDirection flexDirection) { + switch (flexDirection) { + case FlexDirection::Column: + return PhysicalEdge::Bottom; + case FlexDirection::ColumnReverse: + return PhysicalEdge::Top; + case FlexDirection::Row: + return PhysicalEdge::Right; + case FlexDirection::RowReverse: + return PhysicalEdge::Left; + } + + fatalWithMessage("Invalid FlexDirection"); +} + +inline PhysicalEdge inlineStartEdge( + FlexDirection flexDirection, + Direction direction) { + if (isRow(flexDirection)) { + return direction == Direction::RTL ? PhysicalEdge::Right + : PhysicalEdge::Left; + } + + return PhysicalEdge::Top; +} + +inline PhysicalEdge inlineEndEdge( + FlexDirection flexDirection, + Direction direction) { + if (isRow(flexDirection)) { + return direction == Direction::RTL ? PhysicalEdge::Left + : PhysicalEdge::Right; + } + + return PhysicalEdge::Bottom; +} + +inline Dimension dimension(FlexDirection flexDirection) { + switch (flexDirection) { + case FlexDirection::Column: + return Dimension::Height; + case FlexDirection::ColumnReverse: + return Dimension::Height; + case FlexDirection::Row: + return Dimension::Width; + case FlexDirection::RowReverse: + return Dimension::Width; + } + + fatalWithMessage("Invalid FlexDirection"); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/FlexLine.cpp b/Source/3rdParty/yoga/algorithm/FlexLine.cpp new file mode 100644 index 000000000..2d0dfd356 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/FlexLine.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/Yoga.h" + +#include "yoga/algorithm/BoundAxis.h" +#include "yoga/algorithm/FlexDirection.h" +#include "yoga/algorithm/FlexLine.h" + +namespace facebook::yoga { + +FlexLine calculateFlexLine( + yoga::Node* const node, + const Direction ownerDirection, + const float mainAxisownerSize, + const float availableInnerWidth, + const float availableInnerMainDim, + const size_t startOfLineIndex, + const size_t lineCount) { + std::vector itemsInFlow; + itemsInFlow.reserve(node->getChildren().size()); + + float sizeConsumed = 0.0f; + float totalFlexGrowFactors = 0.0f; + float totalFlexShrinkScaledFactors = 0.0f; + size_t numberOfAutoMargins = 0; + size_t endOfLineIndex = startOfLineIndex; + size_t firstElementInLineIndex = startOfLineIndex; + + float sizeConsumedIncludingMinConstraint = 0; + const FlexDirection mainAxis = resolveDirection( + node->style().flexDirection(), node->resolveDirection(ownerDirection)); + const bool isNodeFlexWrap = node->style().flexWrap() != Wrap::NoWrap; + const float gap = + node->style().computeGapForAxis(mainAxis, availableInnerMainDim); + + // Add items to the current line until it's full or we run out of items. + for (; endOfLineIndex < node->getChildren().size(); endOfLineIndex++) { + auto child = node->getChild(endOfLineIndex); + if (child->style().display() == Display::None || + child->style().positionType() == PositionType::Absolute) { + if (firstElementInLineIndex == endOfLineIndex) { + // We haven't found the first contributing element in the line yet. + firstElementInLineIndex++; + } + continue; + } + + if (child->style().flexStartMarginIsAuto(mainAxis, ownerDirection)) { + numberOfAutoMargins++; + } + if (child->style().flexEndMarginIsAuto(mainAxis, ownerDirection)) { + numberOfAutoMargins++; + } + + const bool isFirstElementInLine = + (endOfLineIndex - firstElementInLineIndex) == 0; + + child->setLineIndex(lineCount); + const float childMarginMainAxis = + child->style().computeMarginForAxis(mainAxis, availableInnerWidth); + const float childLeadingGapMainAxis = isFirstElementInLine ? 0.0f : gap; + const float flexBasisWithMinAndMaxConstraints = + boundAxisWithinMinAndMax( + child, + mainAxis, + child->getLayout().computedFlexBasis, + mainAxisownerSize) + .unwrap(); + + // If this is a multi-line flow and this item pushes us over the available + // size, we've hit the end of the current line. Break out of the loop and + // lay out the current line. + if (sizeConsumedIncludingMinConstraint + flexBasisWithMinAndMaxConstraints + + childMarginMainAxis + childLeadingGapMainAxis > + availableInnerMainDim && + isNodeFlexWrap && !itemsInFlow.empty()) { + break; + } + + sizeConsumedIncludingMinConstraint += flexBasisWithMinAndMaxConstraints + + childMarginMainAxis + childLeadingGapMainAxis; + sizeConsumed += flexBasisWithMinAndMaxConstraints + childMarginMainAxis + + childLeadingGapMainAxis; + + if (child->isNodeFlexible()) { + totalFlexGrowFactors += child->resolveFlexGrow(); + + // Unlike the grow factor, the shrink factor is scaled relative to the + // child dimension. + totalFlexShrinkScaledFactors += -child->resolveFlexShrink() * + child->getLayout().computedFlexBasis.unwrap(); + } + + itemsInFlow.push_back(child); + } + + // The total flex factor needs to be floored to 1. + if (totalFlexGrowFactors > 0 && totalFlexGrowFactors < 1) { + totalFlexGrowFactors = 1; + } + + // The total flex shrink factor needs to be floored to 1. + if (totalFlexShrinkScaledFactors > 0 && totalFlexShrinkScaledFactors < 1) { + totalFlexShrinkScaledFactors = 1; + } + + return FlexLine{ + .itemsInFlow = std::move(itemsInFlow), + .sizeConsumed = sizeConsumed, + .endOfLineIndex = endOfLineIndex, + .numberOfAutoMargins = numberOfAutoMargins, + .layout = FlexLineRunningLayout{ + totalFlexGrowFactors, + totalFlexShrinkScaledFactors, + }}; +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/FlexLine.h b/Source/3rdParty/yoga/algorithm/FlexLine.h new file mode 100644 index 000000000..727cb9ae9 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/FlexLine.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "yoga/Yoga.h" +#include "yoga/node/Node.h" + +namespace facebook::yoga { + +struct FlexLineRunningLayout { + // Total flex grow factors of flex items which are to be laid in the current + // line. This is decremented as free space is distributed. + float totalFlexGrowFactors{0.0f}; + + // Total flex shrink factors of flex items which are to be laid in the current + // line. This is decremented as free space is distributed. + float totalFlexShrinkScaledFactors{0.0f}; + + // The amount of available space within inner dimensions of the line which may + // still be distributed. + float remainingFreeSpace{0.0f}; + + // The size of the mainDim for the row after considering size, padding, margin + // and border of flex items. This is used to calculate maxLineDim after going + // through all the rows to decide on the main axis size of owner. + float mainDim{0.0f}; + + // The size of the crossDim for the row after considering size, padding, + // margin and border of flex items. Used for calculating containers crossSize. + float crossDim{0.0f}; +}; + +struct FlexLine { + // List of children which are part of the line flow. This means they are not + // positioned absolutely, or with `display: "none"`, and do not overflow the + // available dimensions. + const std::vector itemsInFlow{}; + + // Accumulation of the dimensions and margin of all the children on the + // current line. This will be used in order to either set the dimensions of + // the node if none already exist or to compute the remaining space left for + // the flexible children. + const float sizeConsumed{0.0f}; + + // The index of the first item beyond the current line. + const size_t endOfLineIndex{0}; + + // Number of edges along the line flow with an auto margin. + const size_t numberOfAutoMargins{0}; + + // Layout information about the line computed in steps after line-breaking + FlexLineRunningLayout layout{}; +}; + +// Calculates where a line starting at a given index should break, returning +// information about the collective children on the liune. +// +// This function assumes that all the children of node have their +// computedFlexBasis properly computed(To do this use +// computeFlexBasisForChildren function). +FlexLine calculateFlexLine( + yoga::Node* node, + Direction ownerDirection, + float mainAxisownerSize, + float availableInnerWidth, + float availableInnerMainDim, + size_t startOfLineIndex, + size_t lineCount); + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/PixelGrid.cpp b/Source/3rdParty/yoga/algorithm/PixelGrid.cpp new file mode 100644 index 000000000..a487132ef --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/PixelGrid.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/Yoga.h" + +#include "yoga/algorithm/PixelGrid.h" +#include "yoga/numeric/Comparison.h" + +namespace facebook::yoga { + +float roundValueToPixelGrid( + const double value, + const double pointScaleFactor, + const bool forceCeil, + const bool forceFloor) { + double scaledValue = value * pointScaleFactor; + // We want to calculate `fractial` such that `floor(scaledValue) = scaledValue + // - fractial`. + double fractial = fmod(scaledValue, 1.0); + if (fractial < 0) { + // This branch is for handling negative numbers for `value`. + // + // Regarding `floor` and `ceil`. Note that for a number x, `floor(x) <= x <= + // ceil(x)` even for negative numbers. Here are a couple of examples: + // - x = 2.2: floor( 2.2) = 2, ceil( 2.2) = 3 + // - x = -2.2: floor(-2.2) = -3, ceil(-2.2) = -2 + // + // Regarding `fmodf`. For fractional negative numbers, `fmodf` returns a + // negative number. For example, `fmodf(-2.2) = -0.2`. However, we want + // `fractial` to be the number such that subtracting it from `value` will + // give us `floor(value)`. In the case of negative numbers, adding 1 to + // `fmodf(value)` gives us this. Let's continue the example from above: + // - fractial = fmodf(-2.2) = -0.2 + // - Add 1 to the fraction: fractial2 = fractial + 1 = -0.2 + 1 = 0.8 + // - Finding the `floor`: -2.2 - fractial2 = -2.2 - 0.8 = -3 + ++fractial; + } + if (yoga::inexactEquals(fractial, 0)) { + // First we check if the value is already rounded + scaledValue = scaledValue - fractial; + } else if (yoga::inexactEquals(fractial, 1.0)) { + scaledValue = scaledValue - fractial + 1.0; + } else if (forceCeil) { + // Next we check if we need to use forced rounding + scaledValue = scaledValue - fractial + 1.0; + } else if (forceFloor) { + scaledValue = scaledValue - fractial; + } else { + // Finally we just round the value + scaledValue = scaledValue - fractial + + (!std::isnan(fractial) && + (fractial > 0.5 || yoga::inexactEquals(fractial, 0.5)) + ? 1.0 + : 0.0); + } + return (std::isnan(scaledValue) || std::isnan(pointScaleFactor)) + ? YGUndefined + : (float)(scaledValue / pointScaleFactor); +} + +void roundLayoutResultsToPixelGrid( + yoga::Node* const node, + const double absoluteLeft, + const double absoluteTop) { + const auto pointScaleFactor = node->getConfig()->getPointScaleFactor(); + + const double nodeLeft = node->getLayout().position(PhysicalEdge::Left); + const double nodeTop = node->getLayout().position(PhysicalEdge::Top); + + const double nodeWidth = node->getLayout().dimension(Dimension::Width); + const double nodeHeight = node->getLayout().dimension(Dimension::Height); + + const double absoluteNodeLeft = absoluteLeft + nodeLeft; + const double absoluteNodeTop = absoluteTop + nodeTop; + + const double absoluteNodeRight = absoluteNodeLeft + nodeWidth; + const double absoluteNodeBottom = absoluteNodeTop + nodeHeight; + + if (pointScaleFactor != 0.0f) { + // If a node has a custom measure function we never want to round down its + // size as this could lead to unwanted text truncation. + const bool textRounding = node->getNodeType() == NodeType::Text; + + node->setLayoutPosition( + roundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding), + PhysicalEdge::Left); + + node->setLayoutPosition( + roundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding), + PhysicalEdge::Top); + + // We multiply dimension by scale factor and if the result is close to the + // whole number, we don't have any fraction To verify if the result is close + // to whole number we want to check both floor and ceil numbers + const bool hasFractionalWidth = + !yoga::inexactEquals(fmod(nodeWidth * pointScaleFactor, 1.0), 0) && + !yoga::inexactEquals(fmod(nodeWidth * pointScaleFactor, 1.0), 1.0); + const bool hasFractionalHeight = + !yoga::inexactEquals(fmod(nodeHeight * pointScaleFactor, 1.0), 0) && + !yoga::inexactEquals(fmod(nodeHeight * pointScaleFactor, 1.0), 1.0); + + node->setLayoutDimension( + roundValueToPixelGrid( + absoluteNodeRight, + pointScaleFactor, + (textRounding && hasFractionalWidth), + (textRounding && !hasFractionalWidth)) - + roundValueToPixelGrid( + absoluteNodeLeft, pointScaleFactor, false, textRounding), + Dimension::Width); + + node->setLayoutDimension( + roundValueToPixelGrid( + absoluteNodeBottom, + pointScaleFactor, + (textRounding && hasFractionalHeight), + (textRounding && !hasFractionalHeight)) - + roundValueToPixelGrid( + absoluteNodeTop, pointScaleFactor, false, textRounding), + Dimension::Height); + } + + for (yoga::Node* child : node->getChildren()) { + roundLayoutResultsToPixelGrid(child, absoluteNodeLeft, absoluteNodeTop); + } +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/PixelGrid.h b/Source/3rdParty/yoga/algorithm/PixelGrid.h new file mode 100644 index 000000000..21b50f725 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/PixelGrid.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/Yoga.h" +#include "yoga/node/Node.h" + +namespace facebook::yoga { + +// Round a point value to the nearest physical pixel based on DPI +// (pointScaleFactor) +float roundValueToPixelGrid( + double value, + double pointScaleFactor, + bool forceCeil, + bool forceFloor); + +// Round the layout results of a node and its subtree to the pixel grid. +void roundLayoutResultsToPixelGrid( + yoga::Node* node, + double absoluteLeft, + double absoluteTop); + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/SizingMode.h b/Source/3rdParty/yoga/algorithm/SizingMode.h new file mode 100644 index 000000000..d6eb0310b --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/SizingMode.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/debug/AssertFatal.h" +#include "yoga/enums/MeasureMode.h" + +namespace facebook::yoga { + +/** + * Corresponds to a CSS auto box sizes. Missing "min-content", as Yoga does not + * current support automatic minimum sizes. + * https://www.w3.org/TR/css-sizing-3/#auto-box-sizes + * https://www.w3.org/TR/css-flexbox-1/#min-size-auto + */ +enum class SizingMode { + /** + * The size a box would take if its outer size filled the available space in + * the given axis; in other words, the stretch fit into the available space, + * if that is definite. Undefined if the available space is indefinite. + */ + StretchFit, + + /** + * A box’s “ideal” size in a given axis when given infinite available space. + * Usually this is the smallest size the box could take in that axis while + * still fitting around its contents, i.e. minimizing unfilled space while + * avoiding overflow. + */ + MaxContent, + + /** + * If the available space in a given axis is definite, equal to + * clamp(min-content size, stretch-fit size, max-content size) (i.e. + * max(min-content size, min(max-content size, stretch-fit size))). When + * sizing under a min-content constraint, equal to the min-content size. + * Otherwise, equal to the max-content size in that axis. + */ + FitContent, +}; + +inline MeasureMode measureMode(SizingMode mode) { + switch (mode) { + case SizingMode::StretchFit: + return MeasureMode::Exactly; + case SizingMode::MaxContent: + return MeasureMode::Undefined; + case SizingMode::FitContent: + return MeasureMode::AtMost; + } + + fatalWithMessage("Invalid SizingMode"); +} + +inline SizingMode sizingMode(MeasureMode mode) { + switch (mode) { + case MeasureMode::Exactly: + return SizingMode::StretchFit; + case MeasureMode::Undefined: + return SizingMode::MaxContent; + case MeasureMode::AtMost: + return SizingMode::FitContent; + } + + fatalWithMessage("Invalid MeasureMode"); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/algorithm/TrailingPosition.h b/Source/3rdParty/yoga/algorithm/TrailingPosition.h new file mode 100644 index 000000000..4c1c17927 --- /dev/null +++ b/Source/3rdParty/yoga/algorithm/TrailingPosition.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/Yoga.h" +#include "yoga/algorithm/FlexDirection.h" +#include "yoga/event/event.h" +#include "yoga/node/Node.h" + +namespace facebook::yoga { + +// Given an offset to an edge, returns the offset to the opposite edge on the +// same axis. This assumes that the width/height of both nodes is determined at +// this point. +inline float getPositionOfOppositeEdge( + float position, + FlexDirection axis, + const yoga::Node* const containingNode, + const yoga::Node* const node) { + return containingNode->getLayout().measuredDimension(dimension(axis)) - + node->getLayout().measuredDimension(dimension(axis)) - position; +} + +inline void setChildTrailingPosition( + const yoga::Node* const node, + yoga::Node* const child, + const FlexDirection axis) { + child->setLayoutPosition( + getPositionOfOppositeEdge( + child->getLayout().position(flexStartEdge(axis)), axis, node, child), + flexEndEdge(axis)); +} + +inline bool needsTrailingPosition(const FlexDirection axis) { + return axis == FlexDirection::RowReverse || + axis == FlexDirection::ColumnReverse; +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/config/Config.cpp b/Source/3rdParty/yoga/config/Config.cpp new file mode 100644 index 000000000..ffdb71280 --- /dev/null +++ b/Source/3rdParty/yoga/config/Config.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yoga/config/Config.h" +#include "yoga/debug/Log.h" +#include "yoga/node/Node.h" + +namespace facebook::yoga { + +bool configUpdateInvalidatesLayout( + const Config& oldConfig, + const Config& newConfig) { + return oldConfig.getErrata() != newConfig.getErrata() || + oldConfig.getEnabledExperiments() != newConfig.getEnabledExperiments() || + oldConfig.getPointScaleFactor() != newConfig.getPointScaleFactor() || + oldConfig.useWebDefaults() != newConfig.useWebDefaults(); +} + +void Config::setUseWebDefaults(bool useWebDefaults) { + useWebDefaults_ = useWebDefaults; +} + +bool Config::useWebDefaults() const { + return useWebDefaults_; +} + +void Config::setExperimentalFeatureEnabled( + ExperimentalFeature feature, + bool enabled) { + experimentalFeatures_.set(static_cast(feature), enabled); +} + +bool Config::isExperimentalFeatureEnabled(ExperimentalFeature feature) const { + return experimentalFeatures_.test(static_cast(feature)); +} + +ExperimentalFeatureSet Config::getEnabledExperiments() const { + return experimentalFeatures_; +} + +void Config::setErrata(Errata errata) { + errata_ = errata; +} + +void Config::addErrata(Errata errata) { + errata_ |= errata; +} + +void Config::removeErrata(Errata errata) { + errata_ &= (~errata); +} + +Errata Config::getErrata() const { + return errata_; +} + +bool Config::hasErrata(Errata errata) const { + return (errata_ & errata) != Errata::None; +} + +void Config::setPointScaleFactor(float pointScaleFactor) { + pointScaleFactor_ = pointScaleFactor; +} + +float Config::getPointScaleFactor() const { + return pointScaleFactor_; +} + +void Config::setContext(void* context) { + context_ = context; +} + +void* Config::getContext() const { + return context_; +} + +void Config::setLogger(YGLogger logger) { + logger_ = logger; +} + +void Config::log( + const yoga::Node* node, + LogLevel logLevel, + const char* format, + va_list args) const { + logger_(this, node, unscopedEnum(logLevel), format, args); +} + +void Config::setCloneNodeCallback(YGCloneNodeFunc cloneNode) { + cloneNodeCallback_ = cloneNode; +} + +YGNodeRef Config::cloneNode( + YGNodeConstRef node, + YGNodeConstRef owner, + size_t childIndex) const { + YGNodeRef clone = nullptr; + if (cloneNodeCallback_ != nullptr) { + clone = cloneNodeCallback_(node, owner, childIndex); + } + if (clone == nullptr) { + clone = YGNodeClone(node); + } + return clone; +} + +/*static*/ const Config& Config::getDefault() { + static Config config{getDefaultLogger()}; + return config; +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/config/Config.h b/Source/3rdParty/yoga/config/Config.h new file mode 100644 index 000000000..17c89c853 --- /dev/null +++ b/Source/3rdParty/yoga/config/Config.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "yoga/Yoga.h" +#include "yoga/enums/Errata.h" +#include "yoga/enums/ExperimentalFeature.h" +#include "yoga/enums/LogLevel.h" + +// Tag struct used to form the opaque YGConfigRef for the public C API +struct YGConfig {}; + +namespace facebook::yoga { + +class Config; +class Node; + +using ExperimentalFeatureSet = std::bitset()>; + +// Whether moving a node from an old to new config should dirty previously +// calculated layout results. +bool configUpdateInvalidatesLayout( + const Config& oldConfig, + const Config& newConfig); + +class YG_EXPORT Config : public ::YGConfig { + public: + explicit Config(YGLogger logger) : logger_{logger} {} + + void setUseWebDefaults(bool useWebDefaults); + bool useWebDefaults() const; + + void setExperimentalFeatureEnabled(ExperimentalFeature feature, bool enabled); + bool isExperimentalFeatureEnabled(ExperimentalFeature feature) const; + ExperimentalFeatureSet getEnabledExperiments() const; + + void setErrata(Errata errata); + void addErrata(Errata errata); + void removeErrata(Errata errata); + Errata getErrata() const; + bool hasErrata(Errata errata) const; + + void setPointScaleFactor(float pointScaleFactor); + float getPointScaleFactor() const; + + void setContext(void* context); + void* getContext() const; + + void setLogger(YGLogger logger); + void log( + const yoga::Node* node, + LogLevel logLevel, + const char* format, + va_list args) const; + + void setCloneNodeCallback(YGCloneNodeFunc cloneNode); + YGNodeRef + cloneNode(YGNodeConstRef node, YGNodeConstRef owner, size_t childIndex) const; + + static const Config& getDefault(); + + private: + YGCloneNodeFunc cloneNodeCallback_{nullptr}; + YGLogger logger_{}; + + bool useWebDefaults_ : 1 = false; + + ExperimentalFeatureSet experimentalFeatures_{}; + Errata errata_ = Errata::None; + float pointScaleFactor_ = 1.0f; + void* context_ = nullptr; +}; + +inline Config* resolveRef(const YGConfigRef ref) { + return static_cast(ref); +} + +inline const Config* resolveRef(const YGConfigConstRef ref) { + return static_cast(ref); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Align.h b/Source/3rdParty/yoga/enums/Align.h new file mode 100644 index 000000000..c88eba411 --- /dev/null +++ b/Source/3rdParty/yoga/enums/Align.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Align : uint8_t { + Auto = YGAlignAuto, + FlexStart = YGAlignFlexStart, + Center = YGAlignCenter, + FlexEnd = YGAlignFlexEnd, + Stretch = YGAlignStretch, + Baseline = YGAlignBaseline, + SpaceBetween = YGAlignSpaceBetween, + SpaceAround = YGAlignSpaceAround, + SpaceEvenly = YGAlignSpaceEvenly, +}; + +template <> +constexpr int32_t ordinalCount() { + return 9; +} + +constexpr Align scopedEnum(YGAlign unscoped) { + return static_cast(unscoped); +} + +constexpr YGAlign unscopedEnum(Align scoped) { + return static_cast(scoped); +} + +inline const char* toString(Align e) { + return YGAlignToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Dimension.h b/Source/3rdParty/yoga/enums/Dimension.h new file mode 100644 index 000000000..1b5770812 --- /dev/null +++ b/Source/3rdParty/yoga/enums/Dimension.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Dimension : uint8_t { + Width = YGDimensionWidth, + Height = YGDimensionHeight, +}; + +template <> +constexpr int32_t ordinalCount() { + return 2; +} + +constexpr Dimension scopedEnum(YGDimension unscoped) { + return static_cast(unscoped); +} + +constexpr YGDimension unscopedEnum(Dimension scoped) { + return static_cast(scoped); +} + +inline const char* toString(Dimension e) { + return YGDimensionToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Direction.h b/Source/3rdParty/yoga/enums/Direction.h new file mode 100644 index 000000000..4cad3ec15 --- /dev/null +++ b/Source/3rdParty/yoga/enums/Direction.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Direction : uint8_t { + Inherit = YGDirectionInherit, + LTR = YGDirectionLTR, + RTL = YGDirectionRTL, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr Direction scopedEnum(YGDirection unscoped) { + return static_cast(unscoped); +} + +constexpr YGDirection unscopedEnum(Direction scoped) { + return static_cast(scoped); +} + +inline const char* toString(Direction e) { + return YGDirectionToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Display.h b/Source/3rdParty/yoga/enums/Display.h new file mode 100644 index 000000000..5bef1570d --- /dev/null +++ b/Source/3rdParty/yoga/enums/Display.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Display : uint8_t { + Flex = YGDisplayFlex, + None = YGDisplayNone, +}; + +template <> +constexpr int32_t ordinalCount() { + return 2; +} + +constexpr Display scopedEnum(YGDisplay unscoped) { + return static_cast(unscoped); +} + +constexpr YGDisplay unscopedEnum(Display scoped) { + return static_cast(scoped); +} + +inline const char* toString(Display e) { + return YGDisplayToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Edge.h b/Source/3rdParty/yoga/enums/Edge.h new file mode 100644 index 000000000..65aa498f6 --- /dev/null +++ b/Source/3rdParty/yoga/enums/Edge.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Edge : uint8_t { + Left = YGEdgeLeft, + Top = YGEdgeTop, + Right = YGEdgeRight, + Bottom = YGEdgeBottom, + Start = YGEdgeStart, + End = YGEdgeEnd, + Horizontal = YGEdgeHorizontal, + Vertical = YGEdgeVertical, + All = YGEdgeAll, +}; + +template <> +constexpr int32_t ordinalCount() { + return 9; +} + +constexpr Edge scopedEnum(YGEdge unscoped) { + return static_cast(unscoped); +} + +constexpr YGEdge unscopedEnum(Edge scoped) { + return static_cast(scoped); +} + +inline const char* toString(Edge e) { + return YGEdgeToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Errata.h b/Source/3rdParty/yoga/enums/Errata.h new file mode 100644 index 000000000..f9f907f78 --- /dev/null +++ b/Source/3rdParty/yoga/enums/Errata.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Errata : uint32_t { + None = YGErrataNone, + StretchFlexBasis = YGErrataStretchFlexBasis, + AbsolutePositioningIncorrect = YGErrataAbsolutePositioningIncorrect, + AbsolutePercentAgainstInnerSize = YGErrataAbsolutePercentAgainstInnerSize, + All = YGErrataAll, + Classic = YGErrataClassic, +}; + +YG_DEFINE_ENUM_FLAG_OPERATORS(Errata) + +constexpr Errata scopedEnum(YGErrata unscoped) { + return static_cast(unscoped); +} + +constexpr YGErrata unscopedEnum(Errata scoped) { + return static_cast(scoped); +} + +inline const char* toString(Errata e) { + return YGErrataToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/ExperimentalFeature.h b/Source/3rdParty/yoga/enums/ExperimentalFeature.h new file mode 100644 index 000000000..6e280c24f --- /dev/null +++ b/Source/3rdParty/yoga/enums/ExperimentalFeature.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class ExperimentalFeature : uint8_t { + WebFlexBasis = YGExperimentalFeatureWebFlexBasis, +}; + +template <> +constexpr int32_t ordinalCount() { + return 1; +} + +constexpr ExperimentalFeature scopedEnum(YGExperimentalFeature unscoped) { + return static_cast(unscoped); +} + +constexpr YGExperimentalFeature unscopedEnum(ExperimentalFeature scoped) { + return static_cast(scoped); +} + +inline const char* toString(ExperimentalFeature e) { + return YGExperimentalFeatureToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/FlexDirection.h b/Source/3rdParty/yoga/enums/FlexDirection.h new file mode 100644 index 000000000..b250741f7 --- /dev/null +++ b/Source/3rdParty/yoga/enums/FlexDirection.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class FlexDirection : uint8_t { + Column = YGFlexDirectionColumn, + ColumnReverse = YGFlexDirectionColumnReverse, + Row = YGFlexDirectionRow, + RowReverse = YGFlexDirectionRowReverse, +}; + +template <> +constexpr int32_t ordinalCount() { + return 4; +} + +constexpr FlexDirection scopedEnum(YGFlexDirection unscoped) { + return static_cast(unscoped); +} + +constexpr YGFlexDirection unscopedEnum(FlexDirection scoped) { + return static_cast(scoped); +} + +inline const char* toString(FlexDirection e) { + return YGFlexDirectionToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Gutter.h b/Source/3rdParty/yoga/enums/Gutter.h new file mode 100644 index 000000000..db35e62ee --- /dev/null +++ b/Source/3rdParty/yoga/enums/Gutter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Gutter : uint8_t { + Column = YGGutterColumn, + Row = YGGutterRow, + All = YGGutterAll, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr Gutter scopedEnum(YGGutter unscoped) { + return static_cast(unscoped); +} + +constexpr YGGutter unscopedEnum(Gutter scoped) { + return static_cast(scoped); +} + +inline const char* toString(Gutter e) { + return YGGutterToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Justify.h b/Source/3rdParty/yoga/enums/Justify.h new file mode 100644 index 000000000..8dfbfb36e --- /dev/null +++ b/Source/3rdParty/yoga/enums/Justify.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Justify : uint8_t { + FlexStart = YGJustifyFlexStart, + Center = YGJustifyCenter, + FlexEnd = YGJustifyFlexEnd, + SpaceBetween = YGJustifySpaceBetween, + SpaceAround = YGJustifySpaceAround, + SpaceEvenly = YGJustifySpaceEvenly, +}; + +template <> +constexpr int32_t ordinalCount() { + return 6; +} + +constexpr Justify scopedEnum(YGJustify unscoped) { + return static_cast(unscoped); +} + +constexpr YGJustify unscopedEnum(Justify scoped) { + return static_cast(scoped); +} + +inline const char* toString(Justify e) { + return YGJustifyToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/LogLevel.h b/Source/3rdParty/yoga/enums/LogLevel.h new file mode 100644 index 000000000..910506ac0 --- /dev/null +++ b/Source/3rdParty/yoga/enums/LogLevel.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class LogLevel : uint8_t { + Error = YGLogLevelError, + Warn = YGLogLevelWarn, + Info = YGLogLevelInfo, + Debug = YGLogLevelDebug, + Verbose = YGLogLevelVerbose, + Fatal = YGLogLevelFatal, +}; + +template <> +constexpr int32_t ordinalCount() { + return 6; +} + +constexpr LogLevel scopedEnum(YGLogLevel unscoped) { + return static_cast(unscoped); +} + +constexpr YGLogLevel unscopedEnum(LogLevel scoped) { + return static_cast(scoped); +} + +inline const char* toString(LogLevel e) { + return YGLogLevelToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/MeasureMode.h b/Source/3rdParty/yoga/enums/MeasureMode.h new file mode 100644 index 000000000..c2d866907 --- /dev/null +++ b/Source/3rdParty/yoga/enums/MeasureMode.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class MeasureMode : uint8_t { + Undefined = YGMeasureModeUndefined, + Exactly = YGMeasureModeExactly, + AtMost = YGMeasureModeAtMost, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr MeasureMode scopedEnum(YGMeasureMode unscoped) { + return static_cast(unscoped); +} + +constexpr YGMeasureMode unscopedEnum(MeasureMode scoped) { + return static_cast(scoped); +} + +inline const char* toString(MeasureMode e) { + return YGMeasureModeToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/NodeType.h b/Source/3rdParty/yoga/enums/NodeType.h new file mode 100644 index 000000000..531467308 --- /dev/null +++ b/Source/3rdParty/yoga/enums/NodeType.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class NodeType : uint8_t { + Default = YGNodeTypeDefault, + Text = YGNodeTypeText, +}; + +template <> +constexpr int32_t ordinalCount() { + return 2; +} + +constexpr NodeType scopedEnum(YGNodeType unscoped) { + return static_cast(unscoped); +} + +constexpr YGNodeType unscopedEnum(NodeType scoped) { + return static_cast(scoped); +} + +inline const char* toString(NodeType e) { + return YGNodeTypeToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Overflow.h b/Source/3rdParty/yoga/enums/Overflow.h new file mode 100644 index 000000000..f88bbfc6b --- /dev/null +++ b/Source/3rdParty/yoga/enums/Overflow.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Overflow : uint8_t { + Visible = YGOverflowVisible, + Hidden = YGOverflowHidden, + Scroll = YGOverflowScroll, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr Overflow scopedEnum(YGOverflow unscoped) { + return static_cast(unscoped); +} + +constexpr YGOverflow unscopedEnum(Overflow scoped) { + return static_cast(scoped); +} + +inline const char* toString(Overflow e) { + return YGOverflowToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/PhysicalEdge.h b/Source/3rdParty/yoga/enums/PhysicalEdge.h new file mode 100644 index 000000000..6e02d0401 --- /dev/null +++ b/Source/3rdParty/yoga/enums/PhysicalEdge.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/enums/Edge.h" + +namespace facebook::yoga { + +enum class PhysicalEdge : uint32_t { + Left = yoga::to_underlying(Edge::Left), + Top = yoga::to_underlying(Edge::Top), + Right = yoga::to_underlying(Edge::Right), + Bottom = yoga::to_underlying(Edge::Bottom), +}; + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/PositionType.h b/Source/3rdParty/yoga/enums/PositionType.h new file mode 100644 index 000000000..7ebcd672b --- /dev/null +++ b/Source/3rdParty/yoga/enums/PositionType.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class PositionType : uint8_t { + Static = YGPositionTypeStatic, + Relative = YGPositionTypeRelative, + Absolute = YGPositionTypeAbsolute, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr PositionType scopedEnum(YGPositionType unscoped) { + return static_cast(unscoped); +} + +constexpr YGPositionType unscopedEnum(PositionType scoped) { + return static_cast(scoped); +} + +inline const char* toString(PositionType e) { + return YGPositionTypeToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Unit.h b/Source/3rdParty/yoga/enums/Unit.h new file mode 100644 index 000000000..498334b37 --- /dev/null +++ b/Source/3rdParty/yoga/enums/Unit.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Unit : uint8_t { + Undefined = YGUnitUndefined, + Point = YGUnitPoint, + Percent = YGUnitPercent, + Auto = YGUnitAuto, +}; + +template <> +constexpr int32_t ordinalCount() { + return 4; +} + +constexpr Unit scopedEnum(YGUnit unscoped) { + return static_cast(unscoped); +} + +constexpr YGUnit unscopedEnum(Unit scoped) { + return static_cast(scoped); +} + +inline const char* toString(Unit e) { + return YGUnitToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/Wrap.h b/Source/3rdParty/yoga/enums/Wrap.h new file mode 100644 index 000000000..b81bc4444 --- /dev/null +++ b/Source/3rdParty/yoga/enums/Wrap.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @generated by enums.py +// clang-format off +#pragma once + +#include +#include "yoga/YGEnums.h" +#include "yoga/enums/YogaEnums.h" + +namespace facebook::yoga { + +enum class Wrap : uint8_t { + NoWrap = YGWrapNoWrap, + Wrap = YGWrapWrap, + WrapReverse = YGWrapWrapReverse, +}; + +template <> +constexpr int32_t ordinalCount() { + return 3; +} + +constexpr Wrap scopedEnum(YGWrap unscoped) { + return static_cast(unscoped); +} + +constexpr YGWrap unscopedEnum(Wrap scoped) { + return static_cast(scoped); +} + +inline const char* toString(Wrap e) { + return YGWrapToString(unscopedEnum(e)); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/enums/YogaEnums.h b/Source/3rdParty/yoga/enums/YogaEnums.h new file mode 100644 index 000000000..de446621d --- /dev/null +++ b/Source/3rdParty/yoga/enums/YogaEnums.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook::yoga { + +/** + * Concept for any enum/enum class + */ +template +concept Enumeration = std::is_enum_v; + +/** + * Count of ordinals in a Yoga enum which is sequential + */ +template +constexpr int32_t ordinalCount(); + +/** + * Concept for a yoga enum which is sequential + */ +template +concept HasOrdinality = (ordinalCount() > 0); + +/** + * Count of bits needed to represent every ordinal + */ +template +constexpr int32_t bitCount() { + return std::bit_width( + static_cast>(ordinalCount() - 1)); +} + +/** + * Polyfill of C++ 23 to_underlying() + * https://en.cppreference.com/w/cpp/utility/to_underlying + */ +constexpr auto to_underlying(Enumeration auto e) noexcept { + return static_cast>(e); +} + +/** + * Convenience function to iterate through every value in a Yoga enum as part of + * a range-based for loop. + */ +template +auto ordinals() { + struct Iterator { + EnumT e{}; + + EnumT operator*() const { + return e; + } + + Iterator& operator++() { + e = static_cast(to_underlying(e) + 1); + return *this; + } + + bool operator==(const Iterator& other) const = default; + bool operator!=(const Iterator& other) const = default; + }; + + struct Range { + Iterator begin() const { + return Iterator{}; + } + Iterator end() const { + return Iterator{static_cast(ordinalCount())}; + } + }; + + return Range{}; +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/event/event.cpp b/Source/3rdParty/yoga/event/event.cpp new file mode 100644 index 000000000..e286ded05 --- /dev/null +++ b/Source/3rdParty/yoga/event/event.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "event.h" +#include +#include + +namespace facebook::yoga { + +const char* LayoutPassReasonToString(const LayoutPassReason value) { + switch (value) { + case LayoutPassReason::kInitial: + return "initial"; + case LayoutPassReason::kAbsLayout: + return "abs_layout"; + case LayoutPassReason::kStretch: + return "stretch"; + case LayoutPassReason::kMultilineStretch: + return "multiline_stretch"; + case LayoutPassReason::kFlexLayout: + return "flex_layout"; + case LayoutPassReason::kMeasureChild: + return "measure"; + case LayoutPassReason::kAbsMeasureChild: + return "abs_measure"; + case LayoutPassReason::kFlexMeasure: + return "flex_measure"; + default: + return "unknown"; + } +} + +namespace { + +struct Node { + std::function subscriber = nullptr; + Node* next = nullptr; + + explicit Node(std::function&& subscriber) + : subscriber{std::move(subscriber)} {} +}; + +std::atomic subscribers{nullptr}; + +Node* push(Node* newHead) { + Node* oldHead = nullptr; + do { + oldHead = subscribers.load(std::memory_order_relaxed); + if (newHead != nullptr) { + newHead->next = oldHead; + } + } while (!subscribers.compare_exchange_weak( + oldHead, newHead, std::memory_order_release, std::memory_order_relaxed)); + return oldHead; +} + +} // namespace + +void Event::reset() { + auto head = push(nullptr); + while (head != nullptr) { + auto current = head; + head = head->next; + delete current; + } +} + +void Event::subscribe(std::function&& subscriber) { + push(new Node{std::move(subscriber)}); +} + +void Event::publish( + YGNodeConstRef node, + Type eventType, + const Data& eventData) { + for (auto subscriber = subscribers.load(std::memory_order_relaxed); + subscriber != nullptr; + subscriber = subscriber->next) { + subscriber->subscriber(node, eventType, eventData); + } +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/event/event.h b/Source/3rdParty/yoga/event/event.h new file mode 100644 index 000000000..1a67c0b16 --- /dev/null +++ b/Source/3rdParty/yoga/event/event.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/Yoga.h" + +#include +#include +#include +#include + +namespace facebook::yoga { + +enum struct LayoutType : int { + kLayout = 0, + kMeasure = 1, + kCachedLayout = 2, + kCachedMeasure = 3 +}; + +enum struct LayoutPassReason : int { + kInitial = 0, + kAbsLayout = 1, + kStretch = 2, + kMultilineStretch = 3, + kFlexLayout = 4, + kMeasureChild = 5, + kAbsMeasureChild = 6, + kFlexMeasure = 7, + COUNT +}; + +struct LayoutData { + int layouts; + int measures; + uint32_t maxMeasureCache; + int cachedLayouts; + int cachedMeasures; + int measureCallbacks; + std::array(LayoutPassReason::COUNT)> + measureCallbackReasonsCount; +}; + +const char* LayoutPassReasonToString(LayoutPassReason value); + +struct YG_EXPORT Event { + enum Type { + NodeAllocation, + NodeDeallocation, + NodeLayout, + LayoutPassStart, + LayoutPassEnd, + MeasureCallbackStart, + MeasureCallbackEnd, + NodeBaselineStart, + NodeBaselineEnd, + }; + class Data; + using Subscriber = void(YGNodeConstRef, Type, Data); + using Subscribers = std::vector>; + + template + struct TypedData {}; + + class Data { + const void* data_; + + public: + template + explicit Data(const TypedData& data) : data_{&data} {} + + template + const TypedData& get() const { + return *static_cast*>(data_); + } + }; + + static void reset(); + + static void subscribe(std::function&& subscriber); + + template + static void publish(YGNodeConstRef node, const TypedData& eventData = {}) { + publish(node, E, Data{eventData}); + } + + private: + static void publish( + YGNodeConstRef /*node*/, + Type /*eventType*/, + const Data& /*eventData*/); +}; + +template <> +struct Event::TypedData { + YGConfigConstRef config; +}; + +template <> +struct Event::TypedData { + YGConfigConstRef config; +}; + +template <> +struct Event::TypedData { + LayoutData* layoutData; +}; + +template <> +struct Event::TypedData { + float width; + YGMeasureMode widthMeasureMode; + float height; + YGMeasureMode heightMeasureMode; + float measuredWidth; + float measuredHeight; + const LayoutPassReason reason; +}; + +template <> +struct Event::TypedData { + LayoutType layoutType; +}; + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/node/CachedMeasurement.h b/Source/3rdParty/yoga/node/CachedMeasurement.h new file mode 100644 index 000000000..ec51e248f --- /dev/null +++ b/Source/3rdParty/yoga/node/CachedMeasurement.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "yoga/Yoga.h" + +#include "yoga/algorithm/SizingMode.h" +#include "yoga/numeric/Comparison.h" + +namespace facebook::yoga { + +struct CachedMeasurement { + float availableWidth{-1}; + float availableHeight{-1}; + SizingMode widthSizingMode{SizingMode::MaxContent}; + SizingMode heightSizingMode{SizingMode::MaxContent}; + + float computedWidth{-1}; + float computedHeight{-1}; + + bool operator==(CachedMeasurement measurement) const { + bool isEqual = widthSizingMode == measurement.widthSizingMode && + heightSizingMode == measurement.heightSizingMode; + + if (!yoga::isUndefined(availableWidth) || + !yoga::isUndefined(measurement.availableWidth)) { + isEqual = isEqual && availableWidth == measurement.availableWidth; + } + if (!yoga::isUndefined(availableHeight) || + !yoga::isUndefined(measurement.availableHeight)) { + isEqual = isEqual && availableHeight == measurement.availableHeight; + } + if (!yoga::isUndefined(computedWidth) || + !yoga::isUndefined(measurement.computedWidth)) { + isEqual = isEqual && computedWidth == measurement.computedWidth; + } + if (!yoga::isUndefined(computedHeight) || + !yoga::isUndefined(measurement.computedHeight)) { + isEqual = isEqual && computedHeight == measurement.computedHeight; + } + + return isEqual; + } +}; + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/node/LayoutResults.cpp b/Source/3rdParty/yoga/node/LayoutResults.cpp new file mode 100644 index 000000000..83410c64e --- /dev/null +++ b/Source/3rdParty/yoga/node/LayoutResults.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include "yoga/node/LayoutResults.h" +#include "yoga/numeric/Comparison.h" + +namespace facebook::yoga { + +bool LayoutResults::operator==(LayoutResults layout) const { + bool isEqual = yoga::inexactEquals(position_, layout.position_) && + yoga::inexactEquals(dimensions_, layout.dimensions_) && + yoga::inexactEquals(margin_, layout.margin_) && + yoga::inexactEquals(border_, layout.border_) && + yoga::inexactEquals(padding_, layout.padding_) && + direction() == layout.direction() && + hadOverflow() == layout.hadOverflow() && + lastOwnerDirection == layout.lastOwnerDirection && + nextCachedMeasurementsIndex == layout.nextCachedMeasurementsIndex && + cachedLayout == layout.cachedLayout && + computedFlexBasis == layout.computedFlexBasis; + + for (uint32_t i = 0; i < LayoutResults::MaxCachedMeasurements && isEqual; + ++i) { + isEqual = isEqual && cachedMeasurements[i] == layout.cachedMeasurements[i]; + } + + if (!yoga::isUndefined(measuredDimensions_[0]) || + !yoga::isUndefined(layout.measuredDimensions_[0])) { + isEqual = + isEqual && (measuredDimensions_[0] == layout.measuredDimensions_[0]); + } + if (!yoga::isUndefined(measuredDimensions_[1]) || + !yoga::isUndefined(layout.measuredDimensions_[1])) { + isEqual = + isEqual && (measuredDimensions_[1] == layout.measuredDimensions_[1]); + } + + return isEqual; +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/node/LayoutResults.h b/Source/3rdParty/yoga/node/LayoutResults.h new file mode 100644 index 000000000..3a66212ee --- /dev/null +++ b/Source/3rdParty/yoga/node/LayoutResults.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "yoga/debug/AssertFatal.h" +#include "yoga/enums/Dimension.h" +#include "yoga/enums/Direction.h" +#include "yoga/enums/Edge.h" +#include "yoga/enums/PhysicalEdge.h" +#include "yoga/node/CachedMeasurement.h" +#include "yoga/numeric/FloatOptional.h" + +namespace facebook::yoga { + +struct LayoutResults { + // This value was chosen based on empirical data: + // 98% of analyzed layouts require less than 8 entries. + static constexpr int32_t MaxCachedMeasurements = 8; + + uint32_t computedFlexBasisGeneration = 0; + FloatOptional computedFlexBasis = {}; + + // Instead of recomputing the entire layout every single time, we cache some + // information to break early when nothing changed + uint32_t generationCount = 0; + Direction lastOwnerDirection = Direction::Inherit; + + uint32_t nextCachedMeasurementsIndex = 0; + std::array cachedMeasurements = {}; + + CachedMeasurement cachedLayout{}; + + Direction direction() const { + return direction_; + } + + void setDirection(Direction direction) { + direction_ = direction; + } + + bool hadOverflow() const { + return hadOverflow_; + } + + void setHadOverflow(bool hadOverflow) { + hadOverflow_ = hadOverflow; + } + + float dimension(Dimension axis) const { + return dimensions_[yoga::to_underlying(axis)]; + } + + void setDimension(Dimension axis, float dimension) { + dimensions_[yoga::to_underlying(axis)] = dimension; + } + + float measuredDimension(Dimension axis) const { + return measuredDimensions_[yoga::to_underlying(axis)]; + } + + void setMeasuredDimension(Dimension axis, float dimension) { + measuredDimensions_[yoga::to_underlying(axis)] = dimension; + } + + float position(PhysicalEdge physicalEdge) const { + return position_[yoga::to_underlying(physicalEdge)]; + } + + void setPosition(PhysicalEdge physicalEdge, float dimension) { + position_[yoga::to_underlying(physicalEdge)] = dimension; + } + + float margin(PhysicalEdge physicalEdge) const { + return margin_[yoga::to_underlying(physicalEdge)]; + } + + void setMargin(PhysicalEdge physicalEdge, float dimension) { + margin_[yoga::to_underlying(physicalEdge)] = dimension; + } + + float border(PhysicalEdge physicalEdge) const { + return border_[yoga::to_underlying(physicalEdge)]; + } + + void setBorder(PhysicalEdge physicalEdge, float dimension) { + border_[yoga::to_underlying(physicalEdge)] = dimension; + } + + float padding(PhysicalEdge physicalEdge) const { + return padding_[yoga::to_underlying(physicalEdge)]; + } + + void setPadding(PhysicalEdge physicalEdge, float dimension) { + padding_[yoga::to_underlying(physicalEdge)] = dimension; + } + + bool operator==(LayoutResults layout) const; + bool operator!=(LayoutResults layout) const { + return !(*this == layout); + } + + private: + Direction direction_ : bitCount() = Direction::Inherit; + bool hadOverflow_ : 1 = false; + + std::array dimensions_ = {{YGUndefined, YGUndefined}}; + std::array measuredDimensions_ = {{YGUndefined, YGUndefined}}; + std::array position_ = {}; + std::array margin_ = {}; + std::array border_ = {}; + std::array padding_ = {}; +}; + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/node/Node.cpp b/Source/3rdParty/yoga/node/Node.cpp new file mode 100644 index 000000000..268369f08 --- /dev/null +++ b/Source/3rdParty/yoga/node/Node.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +#include "yoga/debug/AssertFatal.h" +#include "yoga/node/Node.h" +#include "yoga/numeric/Comparison.h" + +namespace facebook::yoga { + +Node::Node() : Node{&Config::getDefault()} {} + +Node::Node(const yoga::Config* config) : config_{config} { + yoga::assertFatal( + config != nullptr, "Attempting to construct Node with null config"); + + if (config->useWebDefaults()) { + useWebDefaults(); + } +} + +Node::Node(Node&& node) noexcept + : hasNewLayout_(node.hasNewLayout_), + isReferenceBaseline_(node.isReferenceBaseline_), + isDirty_(node.isDirty_), + alwaysFormsContainingBlock_(node.alwaysFormsContainingBlock_), + nodeType_(node.nodeType_), + context_(node.context_), + measureFunc_(node.measureFunc_), + baselineFunc_(node.baselineFunc_), + dirtiedFunc_(node.dirtiedFunc_), + style_(std::move(node.style_)), + layout_(node.layout_), + lineIndex_(node.lineIndex_), + owner_(node.owner_), + children_(std::move(node.children_)), + config_(node.config_), + resolvedDimensions_(node.resolvedDimensions_) { + for (auto c : children_) { + c->setOwner(this); + } +} + +YGSize Node::measure( + float width, + MeasureMode widthMode, + float height, + MeasureMode heightMode) { + return measureFunc_( + this, width, unscopedEnum(widthMode), height, unscopedEnum(heightMode)); +} + +float Node::baseline(float width, float height) const { + return baselineFunc_(this, width, height); +} + +float Node::dimensionWithMargin( + const FlexDirection axis, + const float widthSize) { + return getLayout().measuredDimension(dimension(axis)) + + style_.computeMarginForAxis(axis, widthSize); +} + +bool Node::isLayoutDimensionDefined(const FlexDirection axis) { + const float value = getLayout().measuredDimension(dimension(axis)); + return yoga::isDefined(value) && value >= 0.0f; +} + +// Setters + +void Node::setMeasureFunc(YGMeasureFunc measureFunc) { + if (measureFunc == nullptr) { + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate + // places in Litho + setNodeType(NodeType::Default); + } else { + yoga::assertFatalWithNode( + this, + children_.empty(), + "Cannot set measure function: Nodes with measure functions cannot have " + "children."); + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate + // places in Litho + setNodeType(NodeType::Text); + } + + measureFunc_ = measureFunc; +} + +void Node::replaceChild(Node* child, size_t index) { + children_[index] = child; +} + +void Node::replaceChild(Node* oldChild, Node* newChild) { + std::replace(children_.begin(), children_.end(), oldChild, newChild); +} + +void Node::insertChild(Node* child, size_t index) { + children_.insert(children_.begin() + static_cast(index), child); +} + +void Node::setConfig(yoga::Config* config) { + yoga::assertFatal( + config != nullptr, "Attempting to set a null config on a Node"); + yoga::assertFatalWithConfig( + config, + config->useWebDefaults() == config_->useWebDefaults(), + "UseWebDefaults may not be changed after constructing a Node"); + + if (yoga::configUpdateInvalidatesLayout(*config_, *config)) { + markDirtyAndPropagate(); + } + + config_ = config; +} + +void Node::setDirty(bool isDirty) { + if (static_cast(isDirty) == isDirty_) { + return; + } + isDirty_ = isDirty; + if (isDirty && (dirtiedFunc_ != nullptr)) { + dirtiedFunc_(this); + } +} + +bool Node::removeChild(Node* child) { + auto p = std::find(children_.begin(), children_.end(), child); + if (p != children_.end()) { + children_.erase(p); + return true; + } + return false; +} + +void Node::removeChild(size_t index) { + children_.erase(children_.begin() + static_cast(index)); +} + +void Node::setLayoutDirection(Direction direction) { + layout_.setDirection(direction); +} + +void Node::setLayoutMargin(float margin, PhysicalEdge edge) { + layout_.setMargin(edge, margin); +} + +void Node::setLayoutBorder(float border, PhysicalEdge edge) { + layout_.setBorder(edge, border); +} + +void Node::setLayoutPadding(float padding, PhysicalEdge edge) { + layout_.setPadding(edge, padding); +} + +void Node::setLayoutLastOwnerDirection(Direction direction) { + layout_.lastOwnerDirection = direction; +} + +void Node::setLayoutComputedFlexBasis(const FloatOptional computedFlexBasis) { + layout_.computedFlexBasis = computedFlexBasis; +} + +void Node::setLayoutPosition(float position, PhysicalEdge edge) { + layout_.setPosition(edge, position); +} + +void Node::setLayoutComputedFlexBasisGeneration( + uint32_t computedFlexBasisGeneration) { + layout_.computedFlexBasisGeneration = computedFlexBasisGeneration; +} + +void Node::setLayoutMeasuredDimension( + float measuredDimension, + Dimension dimension) { + layout_.setMeasuredDimension(dimension, measuredDimension); +} + +void Node::setLayoutHadOverflow(bool hadOverflow) { + layout_.setHadOverflow(hadOverflow); +} + +void Node::setLayoutDimension(float LengthValue, Dimension dimension) { + layout_.setDimension(dimension, LengthValue); +} + +// If both left and right are defined, then use left. Otherwise return +left or +// -right depending on which is defined. Ignore statically positioned nodes as +// insets do not apply to them. +float Node::relativePosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + if (style_.positionType() == PositionType::Static) { + return 0; + } + if (style_.isInlineStartPositionDefined(axis, direction)) { + return style_.computeInlineStartPosition(axis, direction, axisSize); + } + + return -1 * style_.computeInlineEndPosition(axis, direction, axisSize); +} + +void Node::setPosition( + const Direction direction, + const float mainSize, + const float crossSize, + const float ownerWidth) { + /* Root nodes should be always layouted as LTR, so we don't return negative + * values. */ + const Direction directionRespectingRoot = + owner_ != nullptr ? direction : Direction::LTR; + const FlexDirection mainAxis = + yoga::resolveDirection(style_.flexDirection(), directionRespectingRoot); + const FlexDirection crossAxis = + yoga::resolveCrossDirection(mainAxis, directionRespectingRoot); + + // In the case of position static these are just 0. See: + // https://www.w3.org/TR/css-position-3/#valdef-position-static + const float relativePositionMain = + relativePosition(mainAxis, directionRespectingRoot, mainSize); + const float relativePositionCross = + relativePosition(crossAxis, directionRespectingRoot, crossSize); + + const auto mainAxisLeadingEdge = inlineStartEdge(mainAxis, direction); + const auto mainAxisTrailingEdge = inlineEndEdge(mainAxis, direction); + const auto crossAxisLeadingEdge = inlineStartEdge(crossAxis, direction); + const auto crossAxisTrailingEdge = inlineEndEdge(crossAxis, direction); + + setLayoutPosition( + (style_.computeInlineStartMargin(mainAxis, direction, ownerWidth) + + relativePositionMain), + mainAxisLeadingEdge); + setLayoutPosition( + (style_.computeInlineEndMargin(mainAxis, direction, ownerWidth) + + relativePositionMain), + mainAxisTrailingEdge); + setLayoutPosition( + (style_.computeInlineStartMargin(crossAxis, direction, ownerWidth) + + relativePositionCross), + crossAxisLeadingEdge); + setLayoutPosition( + (style_.computeInlineEndMargin(crossAxis, direction, ownerWidth) + + relativePositionCross), + crossAxisTrailingEdge); +} + +Style::Length Node::resolveFlexBasisPtr() const { + Style::Length flexBasis = style_.flexBasis(); + if (flexBasis.unit() != Unit::Auto && flexBasis.unit() != Unit::Undefined) { + return flexBasis; + } + if (style_.flex().isDefined() && style_.flex().unwrap() > 0.0f) { + return config_->useWebDefaults() ? value::ofAuto() : value::points(0); + } + return value::ofAuto(); +} + +void Node::resolveDimension() { + for (auto dim : {Dimension::Width, Dimension::Height}) { + if (style_.maxDimension(dim).isDefined() && + yoga::inexactEquals( + style_.maxDimension(dim), style_.minDimension(dim))) { + resolvedDimensions_[yoga::to_underlying(dim)] = style_.maxDimension(dim); + } else { + resolvedDimensions_[yoga::to_underlying(dim)] = style_.dimension(dim); + } + } +} + +Direction Node::resolveDirection(const Direction ownerDirection) { + if (style_.direction() == Direction::Inherit) { + return ownerDirection != Direction::Inherit ? ownerDirection + : Direction::LTR; + } else { + return style_.direction(); + } +} + +void Node::clearChildren() { + children_.clear(); + children_.shrink_to_fit(); +} + +// Other Methods + +void Node::cloneChildrenIfNeeded() { + size_t i = 0; + for (Node*& child : children_) { + if (child->getOwner() != this) { + child = resolveRef(config_->cloneNode(child, this, i)); + child->setOwner(this); + } + i += 1; + } +} + +void Node::markDirtyAndPropagate() { + if (!isDirty_) { + setDirty(true); + setLayoutComputedFlexBasis(FloatOptional()); + if (owner_ != nullptr) { + owner_->markDirtyAndPropagate(); + } + } +} + +float Node::resolveFlexGrow() const { + // Root nodes flexGrow should always be 0 + if (owner_ == nullptr) { + return 0.0; + } + if (style_.flexGrow().isDefined()) { + return style_.flexGrow().unwrap(); + } + if (style_.flex().isDefined() && style_.flex().unwrap() > 0.0f) { + return style_.flex().unwrap(); + } + return Style::DefaultFlexGrow; +} + +float Node::resolveFlexShrink() const { + if (owner_ == nullptr) { + return 0.0; + } + if (style_.flexShrink().isDefined()) { + return style_.flexShrink().unwrap(); + } + if (!config_->useWebDefaults() && style_.flex().isDefined() && + style_.flex().unwrap() < 0.0f) { + return -style_.flex().unwrap(); + } + return config_->useWebDefaults() ? Style::WebDefaultFlexShrink + : Style::DefaultFlexShrink; +} + +bool Node::isNodeFlexible() { + return ( + (style_.positionType() != PositionType::Absolute) && + (resolveFlexGrow() != 0 || resolveFlexShrink() != 0)); +} + +void Node::reset() { + yoga::assertFatalWithNode( + this, + children_.empty(), + "Cannot reset a node which still has children attached"); + yoga::assertFatalWithNode( + this, owner_ == nullptr, "Cannot reset a node still attached to a owner"); + + *this = Node{getConfig()}; +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/node/Node.h b/Source/3rdParty/yoga/node/Node.h new file mode 100644 index 000000000..b1242e2d5 --- /dev/null +++ b/Source/3rdParty/yoga/node/Node.h @@ -0,0 +1,299 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include "yoga/Yoga.h" + +#include "yoga/config/Config.h" +#include "yoga/enums/Dimension.h" +#include "yoga/enums/Direction.h" +#include "yoga/enums/Edge.h" +#include "yoga/enums/Errata.h" +#include "yoga/enums/MeasureMode.h" +#include "yoga/enums/NodeType.h" +#include "yoga/enums/PhysicalEdge.h" +#include "yoga/node/LayoutResults.h" +#include "yoga/style/Style.h" + +// Tag struct used to form the opaque YGNodeRef for the public C API +struct YGNode {}; + +namespace facebook::yoga { + +class YG_EXPORT Node : public ::YGNode { + public: + Node(); + explicit Node(const Config* config); + + Node(Node&& node) noexcept; + + // Does not expose true value semantics, as children are not cloned eagerly. + // Should we remove this? + Node(const Node& node) = default; + + // assignment means potential leaks of existing children, or alternatively + // freeing unowned memory, double free, or freeing stack memory. + Node& operator=(const Node&) = delete; + + // Getters + void* getContext() const { + return context_; + } + + bool alwaysFormsContainingBlock() const { + return alwaysFormsContainingBlock_; + } + + bool getHasNewLayout() const { + return hasNewLayout_; + } + + NodeType getNodeType() const { + return nodeType_; + } + + bool hasMeasureFunc() const noexcept { + return measureFunc_ != nullptr; + } + + YGSize measure( + float width, + MeasureMode widthMode, + float height, + MeasureMode heightMode); + + bool hasBaselineFunc() const noexcept { + return baselineFunc_ != nullptr; + } + + float baseline(float width, float height) const; + + float dimensionWithMargin(FlexDirection axis, float widthSize); + + bool isLayoutDimensionDefined(FlexDirection axis); + + /** + * Whether the node has a "definite length" along the given axis. + * https://www.w3.org/TR/css-sizing-3/#definite + */ + inline bool hasDefiniteLength(Dimension dimension, float ownerSize) { + auto usedValue = getResolvedDimension(dimension).resolve(ownerSize); + return usedValue.isDefined() && usedValue.unwrap() >= 0.0f; + } + + bool hasErrata(Errata errata) const { + return config_->hasErrata(errata); + } + + YGDirtiedFunc getDirtiedFunc() const { + return dirtiedFunc_; + } + + // For Performance reasons passing as reference. + Style& style() { + return style_; + } + + const Style& style() const { + return style_; + } + + // For Performance reasons passing as reference. + LayoutResults& getLayout() { + return layout_; + } + + const LayoutResults& getLayout() const { + return layout_; + } + + size_t getLineIndex() const { + return lineIndex_; + } + + bool isReferenceBaseline() const { + return isReferenceBaseline_; + } + + // returns the Node that owns this Node. An owner is used to identify + // the YogaTree that a Node belongs to. This method will return the parent + // of the Node when a Node only belongs to one YogaTree or nullptr when + // the Node is shared between two or more YogaTrees. + Node* getOwner() const { + return owner_; + } + + const std::vector& getChildren() const { + return children_; + } + + Node* getChild(size_t index) const { + return children_.at(index); + } + + size_t getChildCount() const { + return children_.size(); + } + + const Config* getConfig() const { + return config_; + } + + bool isDirty() const { + return isDirty_; + } + + std::array getResolvedDimensions() const { + return resolvedDimensions_; + } + + Style::Length getResolvedDimension(Dimension dimension) const { + return resolvedDimensions_[static_cast(dimension)]; + } + + // Setters + + void setContext(void* context) { + context_ = context; + } + + void setAlwaysFormsContainingBlock(bool alwaysFormsContainingBlock) { + alwaysFormsContainingBlock_ = alwaysFormsContainingBlock; + } + + void setHasNewLayout(bool hasNewLayout) { + hasNewLayout_ = hasNewLayout; + } + + void setNodeType(NodeType nodeType) { + nodeType_ = nodeType; + } + + void setMeasureFunc(YGMeasureFunc measureFunc); + + void setBaselineFunc(YGBaselineFunc baseLineFunc) { + baselineFunc_ = baseLineFunc; + } + + void setDirtiedFunc(YGDirtiedFunc dirtiedFunc) { + dirtiedFunc_ = dirtiedFunc; + } + + void setStyle(const Style& style) { + style_ = style; + } + + void setLayout(const LayoutResults& layout) { + layout_ = layout; + } + + void setLineIndex(size_t lineIndex) { + lineIndex_ = lineIndex; + } + + void setIsReferenceBaseline(bool isReferenceBaseline) { + isReferenceBaseline_ = isReferenceBaseline; + } + + void setOwner(Node* owner) { + owner_ = owner; + } + + void setChildren(const std::vector& children) { + children_ = children; + } + + // TODO: rvalue override for setChildren + + void setConfig(Config* config); + + void setDirty(bool isDirty); + void setLayoutLastOwnerDirection(Direction direction); + void setLayoutComputedFlexBasis(FloatOptional computedFlexBasis); + void setLayoutComputedFlexBasisGeneration( + uint32_t computedFlexBasisGeneration); + void setLayoutMeasuredDimension(float measuredDimension, Dimension dimension); + void setLayoutHadOverflow(bool hadOverflow); + void setLayoutDimension(float LengthValue, Dimension dimension); + void setLayoutDirection(Direction direction); + void setLayoutMargin(float margin, PhysicalEdge edge); + void setLayoutBorder(float border, PhysicalEdge edge); + void setLayoutPadding(float padding, PhysicalEdge edge); + void setLayoutPosition(float position, PhysicalEdge edge); + void setPosition( + Direction direction, + float mainSize, + float crossSize, + float ownerWidth); + + // Other methods + Style::Length resolveFlexBasisPtr() const; + void resolveDimension(); + Direction resolveDirection(Direction ownerDirection); + void clearChildren(); + /// Replaces the occurrences of oldChild with newChild + void replaceChild(Node* oldChild, Node* newChild); + void replaceChild(Node* child, size_t index); + void insertChild(Node* child, size_t index); + /// Removes the first occurrence of child + bool removeChild(Node* child); + void removeChild(size_t index); + + void cloneChildrenIfNeeded(); + void markDirtyAndPropagate(); + float resolveFlexGrow() const; + float resolveFlexShrink() const; + bool isNodeFlexible(); + void reset(); + + private: + // Used to allow resetting the node + Node& operator=(Node&&) noexcept = default; + + float relativePosition( + FlexDirection axis, + Direction direction, + float axisSize) const; + + void useWebDefaults() { + style_.setFlexDirection(FlexDirection::Row); + style_.setAlignContent(Align::Stretch); + } + + bool hasNewLayout_ : 1 = true; + bool isReferenceBaseline_ : 1 = false; + bool isDirty_ : 1 = true; + bool alwaysFormsContainingBlock_ : 1 = false; + NodeType nodeType_ : bitCount() = NodeType::Default; + void* context_ = nullptr; + YGMeasureFunc measureFunc_ = nullptr; + YGBaselineFunc baselineFunc_ = nullptr; + YGDirtiedFunc dirtiedFunc_ = nullptr; + Style style_; + LayoutResults layout_; + size_t lineIndex_ = 0; + Node* owner_ = nullptr; + std::vector children_; + const Config* config_; + std::array resolvedDimensions_{ + {value::undefined(), value::undefined()}}; +}; + +inline Node* resolveRef(const YGNodeRef ref) { + return static_cast(ref); +} + +inline const Node* resolveRef(const YGNodeConstRef ref) { + return static_cast(ref); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/numeric/Comparison.h b/Source/3rdParty/yoga/numeric/Comparison.h new file mode 100644 index 000000000..28f866814 --- /dev/null +++ b/Source/3rdParty/yoga/numeric/Comparison.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +#include "yoga/Yoga.h" + +namespace facebook::yoga { + +constexpr bool isUndefined(std::floating_point auto value) { + return value != value; +} + +constexpr bool isDefined(std::floating_point auto value) { + return !isUndefined(value); +} + +/** + * Constexpr version of `std::isinf` before C++ 23 + */ +constexpr bool isinf(auto value) { + return value == +std::numeric_limits::infinity() || + value == -std::numeric_limits::infinity(); +} + +constexpr auto maxOrDefined( + std::floating_point auto a, + std::floating_point auto b) { + if (yoga::isDefined(a) && yoga::isDefined(b)) { + return std::max(a, b); + } + return yoga::isUndefined(a) ? b : a; +} + +constexpr auto minOrDefined( + std::floating_point auto a, + std::floating_point auto b) { + if (yoga::isDefined(a) && yoga::isDefined(b)) { + return std::min(a, b); + } + + return yoga::isUndefined(a) ? b : a; +} + +// Custom equality functions using a hardcoded epsilon of 0.0001f, or returning +// true if both floats are NaN. +inline bool inexactEquals(float a, float b) { + if (yoga::isDefined(a) && yoga::isDefined(b)) { + return std::abs(a - b) < 0.0001f; + } + return yoga::isUndefined(a) && yoga::isUndefined(b); +} + +inline bool inexactEquals(double a, double b) { + if (yoga::isDefined(a) && yoga::isDefined(b)) { + return std::abs(a - b) < 0.0001; + } + return yoga::isUndefined(a) && yoga::isUndefined(b); +} + +template +bool inexactEquals( + const std::array& val1, + const std::array& val2) { + bool areEqual = true; + for (std::size_t i = 0; i < Size && areEqual; ++i) { + areEqual = inexactEquals(val1[i], val2[i]); + } + return areEqual; +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/numeric/FloatOptional.h b/Source/3rdParty/yoga/numeric/FloatOptional.h new file mode 100644 index 000000000..88533336b --- /dev/null +++ b/Source/3rdParty/yoga/numeric/FloatOptional.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/numeric/Comparison.h" +#include + +namespace facebook::yoga { + +struct FloatOptional { + private: + float value_ = std::numeric_limits::quiet_NaN(); + + public: + explicit constexpr FloatOptional(float value) : value_(value) {} + constexpr FloatOptional() = default; + + // returns the wrapped value, or a value x with YGIsUndefined(x) == true + constexpr float unwrap() const { + return value_; + } + + constexpr float unwrapOrDefault(float defaultValue) const { + return isUndefined() ? defaultValue : value_; + } + + constexpr bool isUndefined() const { + return yoga::isUndefined(value_); + } + + constexpr bool isDefined() const { + return yoga::isDefined(value_); + } +}; + +// operators take FloatOptional by value, as it is a 32bit value + +constexpr bool operator==(FloatOptional lhs, FloatOptional rhs) { + return lhs.unwrap() == rhs.unwrap() || + (lhs.isUndefined() && rhs.isUndefined()); +} +constexpr bool operator!=(FloatOptional lhs, FloatOptional rhs) { + return !(lhs == rhs); +} + +constexpr bool operator==(FloatOptional lhs, float rhs) { + return lhs == FloatOptional{rhs}; +} +constexpr bool operator!=(FloatOptional lhs, float rhs) { + return !(lhs == rhs); +} + +constexpr bool operator==(float lhs, FloatOptional rhs) { + return rhs == lhs; +} +constexpr bool operator!=(float lhs, FloatOptional rhs) { + return !(lhs == rhs); +} + +constexpr FloatOptional operator+(FloatOptional lhs, FloatOptional rhs) { + return FloatOptional{lhs.unwrap() + rhs.unwrap()}; +} + +constexpr bool operator>(FloatOptional lhs, FloatOptional rhs) { + return lhs.unwrap() > rhs.unwrap(); +} + +constexpr bool operator<(FloatOptional lhs, FloatOptional rhs) { + return lhs.unwrap() < rhs.unwrap(); +} + +constexpr bool operator>=(FloatOptional lhs, FloatOptional rhs) { + return lhs > rhs || lhs == rhs; +} + +constexpr bool operator<=(FloatOptional lhs, FloatOptional rhs) { + return lhs < rhs || lhs == rhs; +} + +constexpr FloatOptional maxOrDefined(FloatOptional lhs, FloatOptional rhs) { + return FloatOptional{yoga::maxOrDefined(lhs.unwrap(), rhs.unwrap())}; +} + +inline bool inexactEquals(FloatOptional lhs, FloatOptional rhs) { + return yoga::inexactEquals(lhs.unwrap(), rhs.unwrap()); +} + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/style/SmallValueBuffer.h b/Source/3rdParty/yoga/style/SmallValueBuffer.h new file mode 100644 index 000000000..8d78a54dc --- /dev/null +++ b/Source/3rdParty/yoga/style/SmallValueBuffer.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace facebook::yoga { + +// Container which allows storing 32 or 64 bit integer values, whose index may +// never change. Values are first stored in a fixed buffer of `BufferSize` +// 32-bit chunks, before falling back to heap allocation. +template +class SmallValueBuffer { + public: + SmallValueBuffer() = default; + SmallValueBuffer(const SmallValueBuffer& other) { + *this = other; + } + SmallValueBuffer(SmallValueBuffer&& other) noexcept = default; + + // Add a new element to the buffer, returning the index of the element + uint16_t push(uint32_t value) { + const auto index = count_++; + assert(index < 4096 && "SmallValueBuffer can only hold up to 4096 chunks"); + if (index < buffer_.size()) { + buffer_[index] = value; + return index; + } + + if (overflow_ == nullptr) { + overflow_ = std::make_unique(); + } + + overflow_->buffer_.push_back(value); + overflow_->wideElements_.push_back(false); + return index; + } + + uint16_t push(uint64_t value) { + const auto lsb = static_cast(value & 0xFFFFFFFF); + const auto msb = static_cast(value >> 32); + + const auto lsbIndex = push(lsb); + [[maybe_unused]] const auto msbIndex = push(msb); + assert( + msbIndex < 4096 && "SmallValueBuffer can only hold up to 4096 chunks"); + + if (lsbIndex < buffer_.size()) { + wideElements_[lsbIndex] = true; + } else { + overflow_->wideElements_[lsbIndex - buffer_.size()] = true; + } + return lsbIndex; + } + + // Replace an existing element in the buffer with a new value. A new index + // may be returned, e.g. if a new value is wider than the previous. + [[nodiscard]] uint16_t replace(uint16_t index, uint32_t value) { + if (index < buffer_.size()) { + buffer_[index] = value; + } else { + overflow_->buffer_.at(index - buffer_.size()) = value; + } + + return index; + } + + [[nodiscard]] uint16_t replace(uint16_t index, uint64_t value) { + const bool isWide = index < wideElements_.size() + ? wideElements_[index] + : overflow_->wideElements_.at(index - buffer_.size()); + + if (isWide) { + const auto lsb = static_cast(value & 0xFFFFFFFF); + const auto msb = static_cast(value >> 32); + + [[maybe_unused]] auto lsbIndex = replace(index, lsb); + [[maybe_unused]] auto msbIndex = replace(index + 1, msb); + return index; + } else { + return push(value); + } + } + + // Get a value of a given width + uint32_t get32(uint16_t index) const { + if (index < buffer_.size()) { + return buffer_[index]; + } else { + return overflow_->buffer_.at(index - buffer_.size()); + } + } + + uint64_t get64(uint16_t index) const { + const auto lsb = get32(index); + const auto msb = get32(index + 1); + return (static_cast(msb) << 32) | lsb; + } + + SmallValueBuffer& operator=(const SmallValueBuffer& other) { + count_ = other.count_; + buffer_ = other.buffer_; + wideElements_ = other.wideElements_; + overflow_ = other.overflow_ ? std::make_unique(*other.overflow_) + : nullptr; + return *this; + } + + SmallValueBuffer& operator=(SmallValueBuffer&& other) noexcept = default; + + private: + struct Overflow { + std::vector buffer_; + std::vector wideElements_; + }; + + uint16_t count_{0}; + std::array buffer_{}; + std::bitset wideElements_; + std::unique_ptr overflow_; +}; + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/style/Style.h b/Source/3rdParty/yoga/style/Style.h new file mode 100644 index 000000000..0e4f1d54c --- /dev/null +++ b/Source/3rdParty/yoga/style/Style.h @@ -0,0 +1,676 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include "yoga/Yoga.h" + +#include "yoga/algorithm/FlexDirection.h" +#include "yoga/enums/Align.h" +#include "yoga/enums/Dimension.h" +#include "yoga/enums/Direction.h" +#include "yoga/enums/Display.h" +#include "yoga/enums/Edge.h" +#include "yoga/enums/FlexDirection.h" +#include "yoga/enums/Gutter.h" +#include "yoga/enums/Justify.h" +#include "yoga/enums/Overflow.h" +#include "yoga/enums/PhysicalEdge.h" +#include "yoga/enums/PositionType.h" +#include "yoga/enums/Unit.h" +#include "yoga/enums/Wrap.h" +#include "yoga/numeric/FloatOptional.h" +#include "yoga/style/StyleLength.h" +#include "yoga/style/StyleValuePool.h" + +namespace facebook::yoga { + +class YG_EXPORT Style { + public: + using Length = StyleLength; + + static constexpr float DefaultFlexGrow = 0.0f; + static constexpr float DefaultFlexShrink = 0.0f; + static constexpr float WebDefaultFlexShrink = 1.0f; + + Direction direction() const { + return direction_; + } + void setDirection(Direction value) { + direction_ = value; + } + + FlexDirection flexDirection() const { + return flexDirection_; + } + void setFlexDirection(FlexDirection value) { + flexDirection_ = value; + } + + Justify justifyContent() const { + return justifyContent_; + } + void setJustifyContent(Justify value) { + justifyContent_ = value; + } + + Align alignContent() const { + return alignContent_; + } + void setAlignContent(Align value) { + alignContent_ = value; + } + + Align alignItems() const { + return alignItems_; + } + void setAlignItems(Align value) { + alignItems_ = value; + } + + Align alignSelf() const { + return alignSelf_; + } + void setAlignSelf(Align value) { + alignSelf_ = value; + } + + PositionType positionType() const { + return positionType_; + } + void setPositionType(PositionType value) { + positionType_ = value; + } + + Wrap flexWrap() const { + return flexWrap_; + } + void setFlexWrap(Wrap value) { + flexWrap_ = value; + } + + Overflow overflow() const { + return overflow_; + } + void setOverflow(Overflow value) { + overflow_ = value; + } + + Display display() const { + return display_; + } + void setDisplay(Display value) { + display_ = value; + } + + FloatOptional flex() const { + return pool_.getNumber(flex_); + } + void setFlex(FloatOptional value) { + pool_.store(flex_, value); + } + + FloatOptional flexGrow() const { + return pool_.getNumber(flexGrow_); + } + void setFlexGrow(FloatOptional value) { + pool_.store(flexGrow_, value); + } + + FloatOptional flexShrink() const { + return pool_.getNumber(flexShrink_); + } + void setFlexShrink(FloatOptional value) { + pool_.store(flexShrink_, value); + } + + Style::Length flexBasis() const { + return pool_.getLength(flexBasis_); + } + void setFlexBasis(Style::Length value) { + pool_.store(flexBasis_, value); + } + + Style::Length margin(Edge edge) const { + return pool_.getLength(margin_[yoga::to_underlying(edge)]); + } + void setMargin(Edge edge, Style::Length value) { + pool_.store(margin_[yoga::to_underlying(edge)], value); + } + + Style::Length position(Edge edge) const { + return pool_.getLength(position_[yoga::to_underlying(edge)]); + } + void setPosition(Edge edge, Style::Length value) { + pool_.store(position_[yoga::to_underlying(edge)], value); + } + + Style::Length padding(Edge edge) const { + return pool_.getLength(padding_[yoga::to_underlying(edge)]); + } + void setPadding(Edge edge, Style::Length value) { + pool_.store(padding_[yoga::to_underlying(edge)], value); + } + + Style::Length border(Edge edge) const { + return pool_.getLength(border_[yoga::to_underlying(edge)]); + } + void setBorder(Edge edge, Style::Length value) { + pool_.store(border_[yoga::to_underlying(edge)], value); + } + + Style::Length gap(Gutter gutter) const { + return pool_.getLength(gap_[yoga::to_underlying(gutter)]); + } + void setGap(Gutter gutter, Style::Length value) { + pool_.store(gap_[yoga::to_underlying(gutter)], value); + } + + Style::Length dimension(Dimension axis) const { + return pool_.getLength(dimensions_[yoga::to_underlying(axis)]); + } + void setDimension(Dimension axis, Style::Length value) { + pool_.store(dimensions_[yoga::to_underlying(axis)], value); + } + + Style::Length minDimension(Dimension axis) const { + return pool_.getLength(minDimensions_[yoga::to_underlying(axis)]); + } + void setMinDimension(Dimension axis, Style::Length value) { + pool_.store(minDimensions_[yoga::to_underlying(axis)], value); + } + + Style::Length maxDimension(Dimension axis) const { + return pool_.getLength(maxDimensions_[yoga::to_underlying(axis)]); + } + void setMaxDimension(Dimension axis, Style::Length value) { + pool_.store(maxDimensions_[yoga::to_underlying(axis)], value); + } + + FloatOptional aspectRatio() const { + return pool_.getNumber(aspectRatio_); + } + void setAspectRatio(FloatOptional value) { + pool_.store(aspectRatio_, value); + } + + bool horizontalInsetsDefined() const { + return position_[yoga::to_underlying(Edge::Left)].isDefined() || + position_[yoga::to_underlying(Edge::Right)].isDefined() || + position_[yoga::to_underlying(Edge::All)].isDefined() || + position_[yoga::to_underlying(Edge::Horizontal)].isDefined() || + position_[yoga::to_underlying(Edge::Start)].isDefined() || + position_[yoga::to_underlying(Edge::End)].isDefined(); + } + + bool verticalInsetsDefined() const { + return position_[yoga::to_underlying(Edge::Top)].isDefined() || + position_[yoga::to_underlying(Edge::Bottom)].isDefined() || + position_[yoga::to_underlying(Edge::All)].isDefined() || + position_[yoga::to_underlying(Edge::Vertical)].isDefined(); + } + + bool isFlexStartPositionDefined(FlexDirection axis, Direction direction) + const { + return computePosition(flexStartEdge(axis), direction).isDefined(); + } + + bool isInlineStartPositionDefined(FlexDirection axis, Direction direction) + const { + return computePosition(inlineStartEdge(axis, direction), direction) + .isDefined(); + } + + bool isFlexEndPositionDefined(FlexDirection axis, Direction direction) const { + return computePosition(flexEndEdge(axis), direction).isDefined(); + } + + bool isInlineEndPositionDefined(FlexDirection axis, Direction direction) + const { + return computePosition(inlineEndEdge(axis, direction), direction) + .isDefined(); + } + + float computeFlexStartPosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + return computePosition(flexStartEdge(axis), direction) + .resolve(axisSize) + .unwrapOrDefault(0.0f); + } + + float computeInlineStartPosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + return computePosition(inlineStartEdge(axis, direction), direction) + .resolve(axisSize) + .unwrapOrDefault(0.0f); + } + + float computeFlexEndPosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + return computePosition(flexEndEdge(axis), direction) + .resolve(axisSize) + .unwrapOrDefault(0.0f); + } + + float computeInlineEndPosition( + FlexDirection axis, + Direction direction, + float axisSize) const { + return computePosition(inlineEndEdge(axis, direction), direction) + .resolve(axisSize) + .unwrapOrDefault(0.0f); + } + + float computeFlexStartMargin( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeMargin(flexStartEdge(axis), direction) + .resolve(widthSize) + .unwrapOrDefault(0.0f); + } + + float computeInlineStartMargin( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeMargin(inlineStartEdge(axis, direction), direction) + .resolve(widthSize) + .unwrapOrDefault(0.0f); + } + + float computeFlexEndMargin( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeMargin(flexEndEdge(axis), direction) + .resolve(widthSize) + .unwrapOrDefault(0.0f); + } + + float computeInlineEndMargin( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeMargin(inlineEndEdge(axis, direction), direction) + .resolve(widthSize) + .unwrapOrDefault(0.0f); + } + + float computeFlexStartBorder(FlexDirection axis, Direction direction) const { + return maxOrDefined( + computeBorder(flexStartEdge(axis), direction).resolve(0.0f).unwrap(), + 0.0f); + } + + float computeInlineStartBorder(FlexDirection axis, Direction direction) + const { + return maxOrDefined( + computeBorder(inlineStartEdge(axis, direction), direction) + .resolve(0.0f) + .unwrap(), + 0.0f); + } + + float computeFlexEndBorder(FlexDirection axis, Direction direction) const { + return maxOrDefined( + computeBorder(flexEndEdge(axis), direction).resolve(0.0f).unwrap(), + 0.0f); + } + + float computeInlineEndBorder(FlexDirection axis, Direction direction) const { + return maxOrDefined( + computeBorder(inlineEndEdge(axis, direction), direction) + .resolve(0.0f) + .unwrap(), + 0.0f); + } + + float computeFlexStartPadding( + FlexDirection axis, + Direction direction, + float widthSize) const { + return maxOrDefined( + computePadding(flexStartEdge(axis), direction) + .resolve(widthSize) + .unwrap(), + 0.0f); + } + + float computeInlineStartPadding( + FlexDirection axis, + Direction direction, + float widthSize) const { + return maxOrDefined( + computePadding(inlineStartEdge(axis, direction), direction) + .resolve(widthSize) + .unwrap(), + 0.0f); + } + + float computeFlexEndPadding( + FlexDirection axis, + Direction direction, + float widthSize) const { + return maxOrDefined( + computePadding(flexEndEdge(axis), direction) + .resolve(widthSize) + .unwrap(), + 0.0f); + } + + float computeInlineEndPadding( + FlexDirection axis, + Direction direction, + float widthSize) const { + return maxOrDefined( + computePadding(inlineEndEdge(axis, direction), direction) + .resolve(widthSize) + .unwrap(), + 0.0f); + } + + float computeInlineStartPaddingAndBorder( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeInlineStartPadding(axis, direction, widthSize) + + computeInlineStartBorder(axis, direction); + } + + float computeFlexStartPaddingAndBorder( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeFlexStartPadding(axis, direction, widthSize) + + computeFlexStartBorder(axis, direction); + } + + float computeInlineEndPaddingAndBorder( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeInlineEndPadding(axis, direction, widthSize) + + computeInlineEndBorder(axis, direction); + } + + float computeFlexEndPaddingAndBorder( + FlexDirection axis, + Direction direction, + float widthSize) const { + return computeFlexEndPadding(axis, direction, widthSize) + + computeFlexEndBorder(axis, direction); + } + + float computeBorderForAxis(FlexDirection axis) const { + return computeInlineStartBorder(axis, Direction::LTR) + + computeInlineEndBorder(axis, Direction::LTR); + } + + float computeMarginForAxis(FlexDirection axis, float widthSize) const { + // The total margin for a given axis does not depend on the direction + // so hardcoding LTR here to avoid piping direction to this function + return computeInlineStartMargin(axis, Direction::LTR, widthSize) + + computeInlineEndMargin(axis, Direction::LTR, widthSize); + } + + float computeGapForAxis(FlexDirection axis, float ownerSize) const { + auto gap = isRow(axis) ? computeColumnGap() : computeRowGap(); + return maxOrDefined(gap.resolve(ownerSize).unwrap(), 0.0f); + } + + bool flexStartMarginIsAuto(FlexDirection axis, Direction direction) const { + return computeMargin(flexStartEdge(axis), direction).isAuto(); + } + + bool flexEndMarginIsAuto(FlexDirection axis, Direction direction) const { + return computeMargin(flexEndEdge(axis), direction).isAuto(); + } + + bool operator==(const Style& other) const { + return direction_ == other.direction_ && + flexDirection_ == other.flexDirection_ && + justifyContent_ == other.justifyContent_ && + alignContent_ == other.alignContent_ && + alignItems_ == other.alignItems_ && alignSelf_ == other.alignSelf_ && + positionType_ == other.positionType_ && flexWrap_ == other.flexWrap_ && + overflow_ == other.overflow_ && display_ == other.display_ && + numbersEqual(flex_, pool_, other.flex_, other.pool_) && + numbersEqual(flexGrow_, pool_, other.flexGrow_, other.pool_) && + numbersEqual(flexShrink_, pool_, other.flexShrink_, other.pool_) && + lengthsEqual(flexBasis_, pool_, other.flexBasis_, other.pool_) && + lengthsEqual(margin_, pool_, other.margin_, other.pool_) && + lengthsEqual(position_, pool_, other.position_, other.pool_) && + lengthsEqual(padding_, pool_, other.padding_, other.pool_) && + lengthsEqual(border_, pool_, other.border_, other.pool_) && + lengthsEqual(gap_, pool_, other.gap_, other.pool_) && + lengthsEqual(dimensions_, pool_, other.dimensions_, other.pool_) && + lengthsEqual( + minDimensions_, pool_, other.minDimensions_, other.pool_) && + lengthsEqual( + maxDimensions_, pool_, other.maxDimensions_, other.pool_) && + numbersEqual(aspectRatio_, pool_, other.aspectRatio_, other.pool_); + } + + bool operator!=(const Style& other) const { + return !(*this == other); + } + + private: + using Dimensions = std::array()>; + using Edges = std::array()>; + using Gutters = std::array()>; + + static inline bool numbersEqual( + const StyleValueHandle& lhsHandle, + const StyleValuePool& lhsPool, + const StyleValueHandle& rhsHandle, + const StyleValuePool& rhsPool) { + return (lhsHandle.isUndefined() && rhsHandle.isUndefined()) || + (lhsPool.getNumber(lhsHandle) == rhsPool.getNumber(rhsHandle)); + } + + static inline bool lengthsEqual( + const StyleValueHandle& lhsHandle, + const StyleValuePool& lhsPool, + const StyleValueHandle& rhsHandle, + const StyleValuePool& rhsPool) { + return (lhsHandle.isUndefined() && rhsHandle.isUndefined()) || + (lhsPool.getLength(lhsHandle) == rhsPool.getLength(rhsHandle)); + } + + template + static inline bool lengthsEqual( + const std::array& lhs, + const StyleValuePool& lhsPool, + const std::array& rhs, + const StyleValuePool& rhsPool) { + return std::equal( + lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end(), + [&](const auto& lhs, const auto& rhs) { + return lengthsEqual(lhs, lhsPool, rhs, rhsPool); + }); + } + + Style::Length computeColumnGap() const { + if (gap_[yoga::to_underlying(Gutter::Column)].isDefined()) { + return pool_.getLength(gap_[yoga::to_underlying(Gutter::Column)]); + } else { + return pool_.getLength(gap_[yoga::to_underlying(Gutter::All)]); + } + } + + Style::Length computeRowGap() const { + if (gap_[yoga::to_underlying(Gutter::Row)].isDefined()) { + return pool_.getLength(gap_[yoga::to_underlying(Gutter::Row)]); + } else { + return pool_.getLength(gap_[yoga::to_underlying(Gutter::All)]); + } + } + + Style::Length computeLeftEdge(const Edges& edges, Direction layoutDirection) + const { + if (layoutDirection == Direction::LTR && + edges[yoga::to_underlying(Edge::Start)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Start)]); + } else if ( + layoutDirection == Direction::RTL && + edges[yoga::to_underlying(Edge::End)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::End)]); + } else if (edges[yoga::to_underlying(Edge::Left)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Left)]); + } else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Horizontal)]); + } else { + return pool_.getLength(edges[yoga::to_underlying(Edge::All)]); + } + } + + Style::Length computeTopEdge(const Edges& edges) const { + if (edges[yoga::to_underlying(Edge::Top)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Top)]); + } else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Vertical)]); + } else { + return pool_.getLength(edges[yoga::to_underlying(Edge::All)]); + } + } + + Style::Length computeRightEdge(const Edges& edges, Direction layoutDirection) + const { + if (layoutDirection == Direction::LTR && + edges[yoga::to_underlying(Edge::End)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::End)]); + } else if ( + layoutDirection == Direction::RTL && + edges[yoga::to_underlying(Edge::Start)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Start)]); + } else if (edges[yoga::to_underlying(Edge::Right)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Right)]); + } else if (edges[yoga::to_underlying(Edge::Horizontal)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Horizontal)]); + } else { + return pool_.getLength(edges[yoga::to_underlying(Edge::All)]); + } + } + + Style::Length computeBottomEdge(const Edges& edges) const { + if (edges[yoga::to_underlying(Edge::Bottom)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Bottom)]); + } else if (edges[yoga::to_underlying(Edge::Vertical)].isDefined()) { + return pool_.getLength(edges[yoga::to_underlying(Edge::Vertical)]); + } else { + return pool_.getLength(edges[yoga::to_underlying(Edge::All)]); + } + } + + Style::Length computePosition(PhysicalEdge edge, Direction direction) const { + switch (edge) { + case PhysicalEdge::Left: + return computeLeftEdge(position_, direction); + case PhysicalEdge::Top: + return computeTopEdge(position_); + case PhysicalEdge::Right: + return computeRightEdge(position_, direction); + case PhysicalEdge::Bottom: + return computeBottomEdge(position_); + } + + fatalWithMessage("Invalid physical edge"); + } + + Style::Length computeMargin(PhysicalEdge edge, Direction direction) const { + switch (edge) { + case PhysicalEdge::Left: + return computeLeftEdge(margin_, direction); + case PhysicalEdge::Top: + return computeTopEdge(margin_); + case PhysicalEdge::Right: + return computeRightEdge(margin_, direction); + case PhysicalEdge::Bottom: + return computeBottomEdge(margin_); + } + + fatalWithMessage("Invalid physical edge"); + } + + Style::Length computePadding(PhysicalEdge edge, Direction direction) const { + switch (edge) { + case PhysicalEdge::Left: + return computeLeftEdge(padding_, direction); + case PhysicalEdge::Top: + return computeTopEdge(padding_); + case PhysicalEdge::Right: + return computeRightEdge(padding_, direction); + case PhysicalEdge::Bottom: + return computeBottomEdge(padding_); + } + + fatalWithMessage("Invalid physical edge"); + } + + Style::Length computeBorder(PhysicalEdge edge, Direction direction) const { + switch (edge) { + case PhysicalEdge::Left: + return computeLeftEdge(border_, direction); + case PhysicalEdge::Top: + return computeTopEdge(border_); + case PhysicalEdge::Right: + return computeRightEdge(border_, direction); + case PhysicalEdge::Bottom: + return computeBottomEdge(border_); + } + + fatalWithMessage("Invalid physical edge"); + } + + Direction direction_ : bitCount() = Direction::Inherit; + FlexDirection flexDirection_ + : bitCount() = FlexDirection::Column; + Justify justifyContent_ : bitCount() = Justify::FlexStart; + Align alignContent_ : bitCount() = Align::FlexStart; + Align alignItems_ : bitCount() = Align::Stretch; + Align alignSelf_ : bitCount() = Align::Auto; + PositionType positionType_ + : bitCount() = PositionType::Relative; + Wrap flexWrap_ : bitCount() = Wrap::NoWrap; + Overflow overflow_ : bitCount() = Overflow::Visible; + Display display_ : bitCount() = Display::Flex; + + StyleValueHandle flex_{}; + StyleValueHandle flexGrow_{}; + StyleValueHandle flexShrink_{}; + StyleValueHandle flexBasis_{StyleValueHandle::ofAuto()}; + Edges margin_{}; + Edges position_{}; + Edges padding_{}; + Edges border_{}; + Gutters gap_{}; + Dimensions dimensions_{ + StyleValueHandle::ofAuto(), + StyleValueHandle::ofAuto()}; + Dimensions minDimensions_{}; + Dimensions maxDimensions_{}; + StyleValueHandle aspectRatio_{}; + + StyleValuePool pool_; +}; + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/style/StyleLength.h b/Source/3rdParty/yoga/style/StyleLength.h new file mode 100644 index 000000000..e23fa1a64 --- /dev/null +++ b/Source/3rdParty/yoga/style/StyleLength.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "yoga/enums/Unit.h" +#include "yoga/numeric/FloatOptional.h" + +namespace facebook::yoga { + +/** + * Style::Length represents a CSS Value which may be one of: + * 1. Undefined + * 2. A keyword (e.g. auto) + * 3. A CSS value: + * a. value (e.g. 10px) + * b. value of a reference + * 4. (soon) A math function which returns a value + * + * References: + * 1. https://www.w3.org/TR/css-values-4/#lengths + * 2. https://www.w3.org/TR/css-values-4/#percentage-value + * 3. https://www.w3.org/TR/css-values-4/#mixed-percentages + * 4. https://www.w3.org/TR/css-values-4/#math + */ +class StyleLength { + public: + constexpr StyleLength() = default; + + constexpr static StyleLength points(float value) { + return yoga::isUndefined(value) || yoga::isinf(value) + ? undefined() + : StyleLength{FloatOptional{value}, Unit::Point}; + } + + constexpr static StyleLength percent(float value) { + return yoga::isUndefined(value) || yoga::isinf(value) + ? undefined() + : StyleLength{FloatOptional{value}, Unit::Percent}; + } + + constexpr static StyleLength ofAuto() { + return StyleLength{{}, Unit::Auto}; + } + + constexpr static StyleLength undefined() { + return StyleLength{{}, Unit::Undefined}; + } + + constexpr bool isAuto() const { + return unit_ == Unit::Auto; + } + + constexpr bool isUndefined() const { + return unit_ == Unit::Undefined; + } + + constexpr bool isDefined() const { + return !isUndefined(); + } + + constexpr FloatOptional value() const { + return value_; + } + + constexpr Unit unit() const { + return unit_; + } + + constexpr FloatOptional resolve(float referenceLength) { + switch (unit_) { + case Unit::Point: + return value_; + case Unit::Percent: + return FloatOptional{value_.unwrap() * referenceLength * 0.01f}; + default: + return FloatOptional{}; + } + } + + explicit constexpr operator YGValue() const { + return YGValue{value_.unwrap(), unscopedEnum(unit_)}; + } + + constexpr bool operator==(const StyleLength& rhs) const { + return value_ == rhs.value_ && unit_ == rhs.unit_; + } + + private: + // We intentionally do not allow direct construction using value and unit, to + // avoid invalid, or redundant combinations. + constexpr StyleLength(FloatOptional value, Unit unit) + : value_(value), unit_(unit) {} + + FloatOptional value_{}; + Unit unit_{Unit::Undefined}; +}; + +inline bool inexactEquals(const StyleLength& a, const StyleLength& b) { + return a.unit() == b.unit() && inexactEquals(a.value(), b.value()); +} + +namespace value { + +/** + * Canonical unit (one YGUnitPoint) + */ +constexpr StyleLength points(float value) { + return StyleLength::points(value); +} + +/** + * Percent of reference + */ +constexpr StyleLength percent(float value) { + return StyleLength::percent(value); +} + +/** + * "auto" keyword + */ +constexpr StyleLength ofAuto() { + return StyleLength::ofAuto(); +} + +/** + * Undefined + */ +constexpr StyleLength undefined() { + return StyleLength::undefined(); +} + +} // namespace value + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/style/StyleValueHandle.h b/Source/3rdParty/yoga/style/StyleValueHandle.h new file mode 100644 index 000000000..3fb33c850 --- /dev/null +++ b/Source/3rdParty/yoga/style/StyleValueHandle.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "yoga/Yoga.h" + +#include "yoga/numeric/FloatOptional.h" +#include "yoga/style/SmallValueBuffer.h" +#include "yoga/style/StyleLength.h" + +namespace facebook::yoga { + +#pragma pack(push) +#pragma pack(1) + +/** + * StyleValueHandle is a small (16-bit) handle to a length or number in a style. + * The value may be embedded directly in the handle if simple, or the handle may + * instead point to an index within a StyleValuePool. + * + * To read or write a value from a StyleValueHandle, use + * `StyleValuePool::store()`, and `StyleValuePool::getLength()`/ + * `StyleValuePool::getNumber()`. + */ +class StyleValueHandle { + public: + static constexpr StyleValueHandle ofAuto() { + StyleValueHandle handle; + handle.setType(Type::Auto); + return handle; + } + + constexpr bool isUndefined() const { + return type() == Type::Undefined; + } + + constexpr bool isDefined() const { + return !isUndefined(); + } + + constexpr bool isAuto() const { + return type() == Type::Auto; + } + + private: + friend class StyleValuePool; + + static constexpr uint16_t kHandleTypeMask = 0b0000'0000'0000'0111; + static constexpr uint16_t kHandleIndexedMask = 0b0000'0000'0000'1000; + static constexpr uint16_t kHandleValueMask = 0b1111'1111'1111'0000; + + enum class Type : uint8_t { + Undefined, + Point, + Percent, + Number, + Auto, + }; + + constexpr Type type() const { + return static_cast(repr_ & kHandleTypeMask); + } + + constexpr void setType(Type handleType) { + repr_ &= (~kHandleTypeMask); + repr_ |= static_cast(handleType); + } + + constexpr uint16_t value() const { + return repr_ >> 4; + } + + constexpr void setValue(uint16_t value) { + repr_ &= (~kHandleValueMask); + repr_ |= (value << 4); + } + + constexpr bool isValueIndexed() const { + return (repr_ & kHandleIndexedMask) != 0; + } + + constexpr void setValueIsIndexed() { + repr_ |= kHandleIndexedMask; + } + + uint16_t repr_{0}; +}; + +#pragma pack(pop) + +} // namespace facebook::yoga diff --git a/Source/3rdParty/yoga/style/StyleValuePool.h b/Source/3rdParty/yoga/style/StyleValuePool.h new file mode 100644 index 000000000..5cf633a47 --- /dev/null +++ b/Source/3rdParty/yoga/style/StyleValuePool.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include "yoga/numeric/FloatOptional.h" +#include "yoga/style/SmallValueBuffer.h" +#include "yoga/style/StyleLength.h" +#include "yoga/style/StyleValueHandle.h" + +namespace facebook::yoga { + +/** + * StyleValuePool allows compact storage for a sparse collection of assigned + * lengths and numbers. Values are referred to using StyleValueHandle. In most + * cases StyleValueHandle can embed the value directly, but if not, the value is + * stored within a buffer provided by the pool. The pool contains a fixed number + * of inline slots before falling back to heap allocating additional slots. + */ +class StyleValuePool { + public: + void store(StyleValueHandle& handle, StyleLength length) { + if (length.isUndefined()) { + handle.setType(StyleValueHandle::Type::Undefined); + } else if (length.isAuto()) { + handle.setType(StyleValueHandle::Type::Auto); + } else { + auto type = length.unit() == Unit::Point + ? StyleValueHandle::Type::Point + : StyleValueHandle::Type::Percent; + storeValue(handle, length.value().unwrap(), type); + } + } + + void store(StyleValueHandle& handle, FloatOptional number) { + if (number.isUndefined()) { + handle.setType(StyleValueHandle::Type::Undefined); + } else { + storeValue(handle, number.unwrap(), StyleValueHandle::Type::Number); + } + } + + StyleLength getLength(StyleValueHandle handle) const { + if (handle.isUndefined()) { + return value::undefined(); + } else if (handle.isAuto()) { + return value::ofAuto(); + } else { + assert( + handle.type() == StyleValueHandle::Type::Point || + handle.type() == StyleValueHandle::Type::Percent); + float value = (handle.isValueIndexed()) + ? std::bit_cast(buffer_.get32(handle.value())) + : unpackInlineInteger(handle.value()); + + return handle.type() == StyleValueHandle::Type::Point + ? value::points(value) + : value::percent(value); + } + } + + FloatOptional getNumber(StyleValueHandle handle) const { + if (handle.isUndefined()) { + return FloatOptional{}; + } else { + assert(handle.type() == StyleValueHandle::Type::Number); + float value = (handle.isValueIndexed()) + ? std::bit_cast(buffer_.get32(handle.value())) + : unpackInlineInteger(handle.value()); + return FloatOptional{value}; + } + } + + private: + void storeValue( + StyleValueHandle& handle, + float value, + StyleValueHandle::Type type) { + handle.setType(type); + + if (handle.isValueIndexed()) { + auto newIndex = + buffer_.replace(handle.value(), std::bit_cast(value)); + handle.setValue(newIndex); + } else if (isIntegerPackable(value)) { + handle.setValue(packInlineInteger(value)); + } else { + auto newIndex = buffer_.push(std::bit_cast(value)); + handle.setValue(newIndex); + handle.setValueIsIndexed(); + } + } + + static constexpr bool isIntegerPackable(float f) { + constexpr uint16_t kMaxInlineAbsValue = (1 << 11) - 1; + + auto i = static_cast(f); + return static_cast(i) == f && i >= -kMaxInlineAbsValue && + i <= +kMaxInlineAbsValue; + } + + static constexpr uint16_t packInlineInteger(float value) { + uint16_t isNegative = value < 0 ? 1 : 0; + return static_cast( + (isNegative << 11) | + (static_cast(value) * (isNegative != 0u ? -1 : 1))); + } + + static constexpr float unpackInlineInteger(uint16_t value) { + constexpr uint16_t kValueSignMask = 0b0000'1000'0000'0000; + constexpr uint16_t kValueMagnitudeMask = 0b0000'0111'1111'1111; + const bool isNegative = (value & kValueSignMask) != 0; + return static_cast( + (value & kValueMagnitudeMask) * (isNegative ? -1 : 1)); + } + + SmallValueBuffer<4> buffer_; +}; + +} // namespace facebook::yoga diff --git a/Source/Basic/Application.cpp b/Source/Basic/Application.cpp index 1c5b148c7..29f2505ec 100644 --- a/Source/Basic/Application.cpp +++ b/Source/Basic/Application.cpp @@ -29,7 +29,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include -#define DORA_VERSION "1.3.22"_slice +#define DORA_VERSION "1.3.23"_slice #if BX_PLATFORM_ANDROID #include diff --git a/Source/Common/Utils.h b/Source/Common/Utils.h index 7ff44315a..5f084fe79 100644 --- a/Source/Common/Utils.h +++ b/Source/Common/Utils.h @@ -101,6 +101,13 @@ public: \ public: \ void virtual set##funName(bool var) +#define PROPERTY_OVERRIDE_BOOL(funName) \ +public: \ + bool is##funName() const; \ +\ +public: \ + void virtual set##funName(bool var) override + #define PROPERTY_STRING(funName) \ public: \ const std::string& get##funName() const; \ diff --git a/Source/Dora.h b/Source/Dora.h index 5d97e3c4c..807f3ade7 100644 --- a/Source/Dora.h +++ b/Source/Dora.h @@ -61,6 +61,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include "Node/Spine.h" #include "Node/Sprite.h" #include "Node/VGNode.h" +#include "Node/AlignNode.h" #include "Physics/Body.h" #include "Physics/BodyDef.h" #include "Physics/Joint.h" diff --git a/Source/Node/AlignNode.cpp b/Source/Node/AlignNode.cpp new file mode 100644 index 000000000..bdefbed9c --- /dev/null +++ b/Source/Node/AlignNode.cpp @@ -0,0 +1,833 @@ +/* Copyright (c) 2024 Li Jin, dragon-fly@qq.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "Const/Header.h" + +#include "Node/AlignNode.h" + +#include "Basic/Application.h" + +#include "yoga/Yoga.h" + +#include +#include +#include +#include +#include +#include + +NS_DORA_BEGIN + +static std::map parseCssStyle(std::string_view css) { + std::map styles; + auto pairs = css | std::ranges::views::split(';'); + for (auto&& pair : pairs) { + auto keyValue = pair | std::ranges::views::split(':') | std::ranges::views::transform([](auto&& range) { + return Slice{&*range.begin(), s_cast(std::ranges::distance(range))}.trimSpace().toView(); + }); + auto it = keyValue.begin(); + if (it != keyValue.end()) { + std::string_view key = *it; + ++it; + if (it != keyValue.end()) { + std::string_view value = *it; + styles[key] = value; + } + } + } + return styles; +} + +std::optional mapDirection(std::string_view dir) { + switch (Switch::hash(dir)) { + case "ltr"_hash: return YGDirectionLTR; + case "rtl"_hash: return YGDirectionRTL; + case "inherit"_hash: return YGDirectionInherit; + } + Warn("invalid CSS Direction: \"{}\", should be one of \"ltr\", \"rtl\" and \"inherit\"", dir); + return std::nullopt; +} + +std::optional mapAlign(std::string_view align) { + switch (Switch::hash(align)) { + case "flex-start"_hash: return YGAlignFlexStart; + case "center"_hash: return YGAlignCenter; + case "flex-end"_hash: return YGAlignFlexEnd; + case "stretch"_hash: return YGAlignStretch; + case "auto"_hash: return YGAlignAuto; + } + Warn("invalid CSS Align: \"{}\", should be one of \"flex-start\", \"center\", \"flex-end\", \"stretch\" and \"auto\"", align); + return std::nullopt; +} + +std::optional mapFlexDirection(std::string_view flexDirection) { + switch (Switch::hash(flexDirection)) { + case "column"_hash: return YGFlexDirectionColumn; + case "row"_hash: return YGFlexDirectionRow; + case "column-reverse"_hash: return YGFlexDirectionColumnReverse; + case "row-reverse"_hash: return YGFlexDirectionRowReverse; + } + Warn("invalid CSS FlexDirection: \"{}\", should be one of \"column\", \"row\", \"column-reverse\" and \"row-reverse\"", flexDirection); + return std::nullopt; +} + +std::optional mapJustifyContent(std::string_view justifyContent) { + switch (Switch::hash(justifyContent)) { + case "flex-start"_hash: return YGJustifyFlexStart; + case "center"_hash: return YGJustifyCenter; + case "flex-end"_hash: return YGJustifyFlexEnd; + case "space-between"_hash: return YGJustifySpaceBetween; + case "space-around"_hash: return YGJustifySpaceAround; + case "space-evenly"_hash: return YGJustifySpaceEvenly; + } + Warn("invalid CSS JustifyContent: \"{}\", should be one of \"flex-start\", \"center\", \"flex-end\", \"space-between\", \"space-around\" and \"space-evenly\"", justifyContent); + return std::nullopt; +} + +std::optional mapPositionType(std::string_view positionType) { + switch (Switch::hash(positionType)) { + case "absolute"_hash: return YGPositionTypeAbsolute; + case "relative"_hash: return YGPositionTypeRelative; + case "static"_hash: return YGPositionTypeStatic; + } + Warn("invalid CSS PositionType: \"{}\", should be one of \"absolute\", \"relative\" and \"static\"", positionType); + return std::nullopt; +} + +std::optional mapWrap(std::string_view wrap) { + switch (Switch::hash(wrap)) { + case "nowrap"_hash: return YGWrapNoWrap; + case "wrap"_hash: return YGWrapWrap; + case "wrap-reverse"_hash: return YGWrapWrapReverse; + } + Warn("invalid CSS Wrap: \"{}\", should be one of \"no-wrap\", \"wrap\" and \"wrap-reverse\"", wrap); + return std::nullopt; +} + +std::optional mapOverflow(std::string_view overflow) { + switch (Switch::hash(overflow)) { + case "visible"_hash: return YGOverflowVisible; + case "hidden"_hash: return YGOverflowHidden; + case "scroll"_hash: return YGOverflowScroll; + } + Warn("invalid CSS Overflow: \"{}\", should be one of \"visible\", \"hidden\" and \"scroll\"", overflow); + return std::nullopt; +} + +std::optional mapDisplay(std::string_view display) { + switch (Switch::hash(display)) { + case "flex"_hash: return YGDisplayFlex; + case "none"_hash: return YGDisplayNone; + } + Warn("invalid CSS Display: \"{}\", should be one of \"flex\" and \"none\"", display); + return std::nullopt; +} + +static std::optional safeStringToFloat(std::string_view str) { + try { + std::string strValue(str); + return std::stof(strValue); + } catch (const std::invalid_argument&) { + return std::nullopt; + } catch (const std::out_of_range&) { + return std::nullopt; + } + return std::nullopt; +} + +enum class CSSValue { + Number, + Percentage, + Auto +}; + +static std::vector> parseCommaSeparatedValues(std::string_view values, bool allowPercent = false, bool allowAuto = false) { + std::vector> result; + auto parts = values | std::ranges::views::split(','); + for (auto&& part : parts) { + auto partStr = Slice{&*part.begin(), static_cast(std::ranges::distance(part))}.trimSpace(); + if (allowAuto && partStr == "auto") { + result.push_back({CSSValue::Auto, 0}); + } else if (allowPercent && !partStr.empty() && partStr.back() == '%') { + std::string str(partStr); + if (auto num = safeStringToFloat(str)) { + result.push_back({CSSValue::Percentage, num.value()}); + } else { + return {}; + } + } else { + std::string str(partStr); + if (auto num = safeStringToFloat(str)) { + result.push_back({CSSValue::Number, num.value()}); + } else { + return {}; + } + } + } + return result; +} + +static void setEdges(YGNodeRef node, const std::vector>& values, void (*setter)(YGNodeRef, YGEdge, float), void (*percentSetter)(YGNodeRef, YGEdge, float) = nullptr, void (*autoSetter)(YGNodeRef, YGEdge) = nullptr) { + auto setValue = [&](YGEdge edge, const std::pair& value) { + switch (value.first) { + case CSSValue::Number: setter(node, edge, value.second); break; + case CSSValue::Percentage: percentSetter(node, edge, value.second); break; + case CSSValue::Auto: autoSetter(node, edge); break; + default: break; + } + }; + switch (values.size()) { + case 1: + // 单个值,设置所有四个边 + setValue(YGEdgeAll, values[0]); + break; + case 2: + // 两个值,垂直和水平 + setValue(YGEdgeVertical, values[0]); + setValue(YGEdgeHorizontal, values[1]); + break; + case 3: + // 三个值,上,水平,下 + setValue(YGEdgeTop, values[0]); + setValue(YGEdgeHorizontal, values[1]); + setValue(YGEdgeBottom, values[2]); + break; + case 4: + // 四个值,上,右,下,左 + setValue(YGEdgeTop, values[0]); + setValue(YGEdgeRight, values[1]); + setValue(YGEdgeBottom, values[2]); + setValue(YGEdgeLeft, values[3]); + break; + default: + Warn("invalid number of edge values in CSS, should be 1 for all, 2 for vertical and horizontal, 3 for top, horizontal and bottom, 4 for top, right, bottom and left."); + break; + } +} + +void setGap(YGNodeRef node, const std::vector>& values) { + auto setValue = [&](YGGutter gutter, const std::pair& value) { + switch (value.first) { + case CSSValue::Number: + YGNodeStyleSetGap(node, gutter, value.second); + break; + case CSSValue::Percentage: + YGNodeStyleSetGapPercent(node, gutter, value.second); + break; + default: break; + } + }; + switch (values.size()) { + case 1: + // 单个值,应用到所有 gutter + setValue(YGGutterAll, values[0]); + break; + case 2: + // 两个值,依次为行和列 + setValue(YGGutterRow, values[0]); + setValue(YGGutterColumn, values[1]); + break; + default: + Warn("invalid number of gap values in CSS, should be 1 for all, 2 for row and column."); + break; + } +} + +AlignNode::AlignNode(bool isWindowRoot) { + if (isWindowRoot) { + setSize(SharedApplication.getVisualSize()); + float scale = SharedApplication.getDevicePixelRatio(); + setScaleX(scale); + setScaleY(scale); + gslot("AppSizeChanged"_slice, [this](Event*) { + setSize(SharedApplication.getVisualSize()); + float scale = SharedApplication.getDevicePixelRatio(); + setScaleX(scale); + setScaleY(scale); + YGNodeStyleSetWidth(_yogaNode, getWidth()); + YGNodeStyleSetHeight(_yogaNode, getHeight()); + YGNodeCalculateLayout(_yogaNode, YGUndefined, YGUndefined, YGDirectionLTR); + alignLayout(); + }); + } +} + +bool AlignNode::init() { + _yogaNode = YGNodeNewWithConfig(getConfig()); + YGNodeSetContext(_yogaNode, this); + if (getWidth() > 0.0f) { + YGNodeStyleSetWidth(_yogaNode, getWidth()); + } + if (getHeight() > 0.0f) { + YGNodeStyleSetHeight(_yogaNode, getHeight()); + } + return true; +} + +void AlignNode::addChild(Node* child, int order, String tag) { + if (auto alignChild = DoraAs(child)) { + if (!YGNodeGetParent(alignChild->_yogaNode)) { + YGNodeInsertChild(_yogaNode, alignChild->_yogaNode, YGNodeGetChildCount(_yogaNode)); + } + } + Node::addChild(child, order, tag); +} + +void AlignNode::removeChild(Node* child, bool cleanup) { + if (auto alignChild = DoraAs(child)) { + YGNodeRemoveChild(_yogaNode, alignChild->_yogaNode); + } + Node::removeChild(child, cleanup); +} + +void AlignNode::cleanup() { + if (_flags.isOn(Node::Cleanup)) return; + YGNodeFree(_yogaNode); + _yogaNode = nullptr; + Node::cleanup(); +} + +void AlignNode::alignLayout() { + if (!YGNodeGetHasNewLayout(_yogaNode)) { + return; + } + YGNodeSetHasNewLayout(_yogaNode, false); + Size newSize{YGNodeLayoutGetWidth(_yogaNode), YGNodeLayoutGetHeight(_yogaNode)}; + if (getSize() != newSize) { + setSize(newSize); + ARRAY_START(Node, child, _children) { + if (auto alignChild = DoraAs(child)) { + alignChild->setX(YGNodeLayoutGetLeft(alignChild->_yogaNode) + alignChild->getWidth() / 2.0f); + alignChild->setY(getHeight() - (YGNodeLayoutGetTop(alignChild->_yogaNode) + alignChild->getHeight() / 2.0f)); + } + } + } + ARRAY_END + if (YGNodeGetParent(_yogaNode)) { + setX(YGNodeLayoutGetLeft(_yogaNode) + getWidth() / 2.0f); + setY(_parent->getHeight() - (YGNodeLayoutGetTop(_yogaNode) + getHeight() / 2.0f)); + } + emit("AlignLayout"_slice, getWidth(), getHeight()); + for (size_t i = 0; i < YGNodeGetChildCount(_yogaNode); i++) { + auto child = YGNodeGetChild(_yogaNode, i); + auto node = r_cast(YGNodeGetContext(child)); + node->alignLayout(); + } +} + +void AlignNode::visit() { + if (YGNodeIsDirty(_yogaNode)) { + YGNodeCalculateLayout(_yogaNode, YGUndefined, YGUndefined, YGDirectionLTR); + } + alignLayout(); + Node::visit(); +} + +void AlignNode::css(String css) { + auto styles = parseCssStyle(css.toView()); + for (const auto& [key, value] : styles) { + switch (Switch::hash(key)) { + case "direction"_hash: { + if (auto var = mapDirection(value)) { + YGNodeStyleSetDirection(_yogaNode, var.value()); + } + break; + } + case "align-content"_hash: { + if (auto var = mapAlign(value)) { + YGNodeStyleSetAlignContent(_yogaNode, var.value()); + } + break; + } + case "align-items"_hash: { + if (auto var = mapAlign(value)) { + YGNodeStyleSetAlignItems(_yogaNode, var.value()); + } + break; + } + case "align-self"_hash: { + if (auto var = mapAlign(value)) { + YGNodeStyleSetAlignSelf(_yogaNode, var.value()); + } + break; + } + case "flex-direction"_hash: { + if (auto var = mapFlexDirection(value)) { + YGNodeStyleSetFlexDirection(_yogaNode, var.value()); + } + break; + } + case "justify-content"_hash: { + if (auto var = mapJustifyContent(value)) { + YGNodeStyleSetJustifyContent(_yogaNode, var.value()); + } + break; + } + case "flex-wrap"_hash: { + if (auto var = mapWrap(value)) { + YGNodeStyleSetFlexWrap(_yogaNode, var.value()); + } + break; + } + case "flex"_hash: { + if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetFlex(_yogaNode, num.value()); + } else { + Warn("invalid CSS Flex: \"{}\", should be a number", value); + } + break; + } + case "flex-basis"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetFlexBasisPercent(_yogaNode, num.value()); + } + } else if (value == "auto") { + YGNodeStyleSetFlexBasisAuto(_yogaNode); + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetFlexBasis(_yogaNode, num.value()); + } else { + Warn("invalid CSS FlexBasis: \"{}\", should be a number, a percentage or auto", value); + } + break; + } + case "flex-grow"_hash: { + if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetFlexGrow(_yogaNode, num.value()); + } else { + Warn("invalid CSS FlexGrow: \"{}\", should be a number", value); + } + break; + } + case "flex-shrink"_hash: { + if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetFlexShrink(_yogaNode, num.value()); + } else { + Warn("invalid CSS FlexShrink: \"{}\", should be a number", value); + } + break; + } + case "left"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPositionPercent(_yogaNode, YGEdgeLeft, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPosition(_yogaNode, YGEdgeLeft, num.value()); + } else { + Warn("invalid CSS Left: \"{}\", should be a number or a percentage", value); + } + break; + } + case "top"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPositionPercent(_yogaNode, YGEdgeTop, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPosition(_yogaNode, YGEdgeTop, num.value()); + } else { + Warn("invalid CSS Top: \"{}\", should be a number or a percentage", value); + } + break; + } + case "right"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPositionPercent(_yogaNode, YGEdgeRight, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPosition(_yogaNode, YGEdgeRight, num.value()); + } else { + Warn("invalid CSS Right: \"{}\", should be a number or a percentage", value); + } + break; + } + case "bottom"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPositionPercent(_yogaNode, YGEdgeBottom, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPosition(_yogaNode, YGEdgeBottom, num.value()); + } else { + Warn("invalid CSS Bottom: \"{}\", should be a number or a percentage", value); + } + break; + } + case "start"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPositionPercent(_yogaNode, YGEdgeStart, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPosition(_yogaNode, YGEdgeStart, num.value()); + } else { + Warn("invalid CSS Start: \"{}\", should be a number or a percentage", value); + } + break; + } + case "end"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPositionPercent(_yogaNode, YGEdgeEnd, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPosition(_yogaNode, YGEdgeEnd, num.value()); + } else { + Warn("invalid CSS End: \"{}\", should be a number or a percentage", value); + } + break; + } + case "horizontal"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPositionPercent(_yogaNode, YGEdgeHorizontal, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPosition(_yogaNode, YGEdgeHorizontal, num.value()); + } else { + Warn("invalid CSS Horizontal: \"{}\", should be a number or a percentage", value); + } + break; + } + case "vertical"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPositionPercent(_yogaNode, YGEdgeVertical, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPosition(_yogaNode, YGEdgeVertical, num.value()); + } else { + Warn("invalid CSS Vertical: \"{}\", should be a number or a percentage", value); + } + break; + } + case "position"_hash: { + if (auto var = mapPositionType(value)) { + YGNodeStyleSetPositionType(_yogaNode, var.value()); + } + break; + } + case "overflow"_hash: { + if (auto var = mapOverflow(value)) { + YGNodeStyleSetOverflow(_yogaNode, var.value()); + } + break; + } + case "display"_hash: { + if (auto var = mapDisplay(value)) { + YGNodeStyleSetDisplay(_yogaNode, var.value()); + } + break; + } + case "width"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetWidthPercent(_yogaNode, num.value()); + } + } else if (value == "auto") { + YGNodeStyleSetWidthAuto(_yogaNode); + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetWidth(_yogaNode, num.value()); + } else { + Warn("invalid CSS Width: \"{}\", should be a number or a percentage", value); + } + break; + } + case "height"_hash: { + if (value == "auto") { + YGNodeStyleSetHeightAuto(_yogaNode); + } else if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetHeightPercent(_yogaNode, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetHeight(_yogaNode, num.value()); + } else { + Warn("invalid CSS Height: \"{}\", should be a number or a percentage", value); + } + break; + } + case "min-width"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMinWidthPercent(_yogaNode, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMinWidth(_yogaNode, num.value()); + } else { + Warn("invalid CSS MinWidth: \"{}\", should be a number or a percentage", value); + } + break; + } + case "min-height"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMinHeightPercent(_yogaNode, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMinHeight(_yogaNode, num.value()); + } else { + Warn("invalid CSS MinHeight: \"{}\", should be a number or a percentage", value); + } + break; + } + case "max-width"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMaxWidthPercent(_yogaNode, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMaxWidth(_yogaNode, num.value()); + } else { + Warn("invalid CSS MaxWidth: \"{}\", should be a number or a percentage", value); + } + break; + } + case "max-height"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMaxHeightPercent(_yogaNode, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMaxHeight(_yogaNode, num.value()); + } else { + Warn("invalid CSS MaxHeight: \"{}\", should be a number or a percentage", value); + } + break; + } + case "margin"_hash: { + setEdges(_yogaNode, parseCommaSeparatedValues(value, true, true), YGNodeStyleSetMargin, YGNodeStyleSetMarginPercent, YGNodeStyleSetMarginAuto); + break; + } + case "margin-top"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMarginPercent(_yogaNode, YGEdgeTop, num.value()); + } + } else if (value == "auto") { + YGNodeStyleSetMarginAuto(_yogaNode, YGEdgeTop); + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMargin(_yogaNode, YGEdgeTop, num.value()); + } else { + Warn("invalid CSS MarginTop: \"{}\", should be a number, a percentage or auto", value); + } + break; + } + case "margin-right"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMarginPercent(_yogaNode, YGEdgeRight, num.value()); + } + } else if (value == "auto") { + YGNodeStyleSetMarginAuto(_yogaNode, YGEdgeRight); + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMargin(_yogaNode, YGEdgeRight, num.value()); + } else { + Warn("invalid CSS MarginRight: \"{}\", should be a number, a percentage or auto", value); + } + break; + } + case "margin-bottom"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMarginPercent(_yogaNode, YGEdgeBottom, num.value()); + } + } else if (value == "auto") { + YGNodeStyleSetMarginAuto(_yogaNode, YGEdgeBottom); + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMargin(_yogaNode, YGEdgeBottom, num.value()); + } else { + Warn("invalid CSS MarginBottom: \"{}\", should be a number, a percentage or auto", value); + } + break; + } + case "margin-left"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMarginPercent(_yogaNode, YGEdgeLeft, num.value()); + } + } else if (value == "auto") { + YGNodeStyleSetMarginAuto(_yogaNode, YGEdgeLeft); + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMargin(_yogaNode, YGEdgeLeft, num.value()); + } else { + Warn("invalid CSS MarginLeft: \"{}\", should be a number, a percentage or auto", value); + } + break; + } + case "margin-start"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMarginPercent(_yogaNode, YGEdgeStart, num.value()); + } + } else if (value == "auto") { + YGNodeStyleSetMarginAuto(_yogaNode, YGEdgeStart); + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMargin(_yogaNode, YGEdgeStart, num.value()); + } else { + Warn("invalid CSS MarginStart: \"{}\", should be a number, a percentage or auto", value); + } + break; + } + case "margin-end"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetMarginPercent(_yogaNode, YGEdgeEnd, num.value()); + } + } else if (value == "auto") { + YGNodeStyleSetMarginAuto(_yogaNode, YGEdgeEnd); + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetMargin(_yogaNode, YGEdgeEnd, num.value()); + } else { + Warn("invalid CSS MarginEnd: \"{}\", should be a number, a percentage or auto", value); + } + break; + } + case "padding"_hash: { + setEdges(_yogaNode, parseCommaSeparatedValues(value, true), YGNodeStyleSetPadding, YGNodeStyleSetPaddingPercent); + break; + } + case "padding-top"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPaddingPercent(_yogaNode, YGEdgeTop, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPadding(_yogaNode, YGEdgeTop, num.value()); + } else { + Warn("invalid CSS PaddingTop: \"{}\", should be a number or a percentage", value); + } + break; + } + case "padding-right"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPaddingPercent(_yogaNode, YGEdgeRight, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPadding(_yogaNode, YGEdgeRight, num.value()); + } else { + Warn("invalid CSS PaddingRight: \"{}\", should be a number or a percentage", value); + } + break; + } + case "padding-bottom"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPaddingPercent(_yogaNode, YGEdgeBottom, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPadding(_yogaNode, YGEdgeBottom, num.value()); + } else { + Warn("invalid CSS PaddingBottom: \"{}\", should be a number or a percentage", value); + } + break; + } + case "padding-left"_hash: { + if (!value.empty() && value.back() == '%') { + Slice numStr{value}; + numStr.skipRight(1); + if (auto num = safeStringToFloat(numStr.toView())) { + YGNodeStyleSetPaddingPercent(_yogaNode, YGEdgeLeft, num.value()); + } + } else if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetPadding(_yogaNode, YGEdgeLeft, num.value()); + } else { + Warn("invalid CSS PaddingLeft: \"{}\", should be a number or a percentage", value); + } + break; + } + case "border"_hash: { + setEdges(_yogaNode, parseCommaSeparatedValues(value), YGNodeStyleSetBorder); + break; + } + case "gap"_hash: { + setGap(_yogaNode, parseCommaSeparatedValues(value, true)); + break; + } + case "aspect-ratio"_hash: { + if (auto num = safeStringToFloat(value)) { + YGNodeStyleSetAspectRatio(_yogaNode, num.value()); + } else { + Warn("invalid CSS AspectRatio: \"{}\", should be a number", value); + } + break; + } + default: + Warn("invalid CSS Style: \"{}\"", key); + break; + } + } +} + +struct ConfigDeleter { + void operator()(YGConfigRef ptr) const { + YGConfigFree(ptr); + } +}; + +YGConfigRef AlignNode::getConfig() { + static std::unique_ptr config(YGConfigNew()); + static std::once_flag initConfig; + std::call_once(initConfig, []() { + YGConfigSetPointScaleFactor(config.get(), 1.0f); + }); + return config.get(); +} + +NS_DORA_END diff --git a/Source/Node/AlignNode.h b/Source/Node/AlignNode.h new file mode 100644 index 000000000..ac727a629 --- /dev/null +++ b/Source/Node/AlignNode.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2024 Li Jin, dragon-fly@qq.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#pragma once + +#include "Node/Node.h" + +typedef struct YGNode* YGNodeRef; +typedef struct YGConfig* YGConfigRef; + +NS_DORA_BEGIN + +class AlignNode : public Node { +public: + virtual bool init() override; + virtual void visit() override; + virtual void addChild(Node* child, int order, String tag) override; + virtual void removeChild(Node* child, bool cleanup = true) override; + virtual void cleanup() override; + void css(String css); + CREATE_FUNC(AlignNode); + +protected: + AlignNode(bool isWindowRoot = false); + void alignLayout(); + +private: + YGNodeRef _yogaNode = nullptr; + static YGConfigRef getConfig(); + DORA_TYPE_OVERRIDE(AlignNode); +}; + +NS_DORA_END diff --git a/Source/Node/DragonBone.cpp b/Source/Node/DragonBone.cpp index bd14a97c3..a3f347832 100644 --- a/Source/Node/DragonBone.cpp +++ b/Source/Node/DragonBone.cpp @@ -453,10 +453,7 @@ void DragonBone::setShowDebug(bool var) { _debugLine = nullptr; } } -} - -bool DragonBone::isShowDebug() const { - return _debugLine != nullptr; + Node::setShowDebug(var); } DragonBone::DBArmatureProxy* DragonBone::getArmatureProxy() const { diff --git a/Source/Node/DragonBone.h b/Source/Node/DragonBone.h index 5f0cbebb9..2e0d39808 100644 --- a/Source/Node/DragonBone.h +++ b/Source/Node/DragonBone.h @@ -95,15 +95,15 @@ class DragonBone : public Playable { public: PROPERTY_BOOL(DepthWrite); PROPERTY_BOOL(HitTestEnabled); - PROPERTY_BOOL(ShowDebug); PROPERTY_READONLY(DBArmatureProxy*, ArmatureProxy); - virtual void cleanup() override; virtual void visit() override; virtual void render() override; + virtual void cleanup() override; virtual void setSpeed(float var) override; virtual void setRecovery(float var) override; virtual void setFliped(bool var) override; virtual void setLook(String var) override; + virtual void setShowDebug(bool var) override; virtual const std::string& getCurrent() const override; virtual const std::string& getLastCompleted() const override; virtual Vec2 getKeyPoint(String name) const override; diff --git a/Source/Node/DrawNode.cpp b/Source/Node/DrawNode.cpp index 588cf683f..07c29ef97 100644 --- a/Source/Node/DrawNode.cpp +++ b/Source/Node/DrawNode.cpp @@ -567,6 +567,47 @@ void LineRenderer::push(Line* line) { _vertices.insert(_vertices.end(), verts.begin(), verts.end()); } +void LineRenderer::pushRect(PosColorVertex verts[4]) { + uint64_t state = BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_PT_LINESTRIP | BlendFunc::Default.toValue(); + if (state != _lastState) { + render(); + } + _lastState = state; + if (!_vertices.empty()) { + _vertices.reserve(_vertices.size() + 6); + _vertices.push_back(_vertices.back()); + _vertices.back().abgr = 0; + _vertices.push_back(verts[0]); + _vertices.back().abgr = 0; + } else { + _vertices.reserve(_vertices.size() + 4); + } + _vertices.push_back(verts[0]); + _vertices.push_back(verts[1]); + _vertices.push_back(verts[2]); + _vertices.push_back(verts[3]); + _vertices.push_back(verts[0]); +} + +void LineRenderer::pushSegment(PosColorVertex verts[2]) { + uint64_t state = BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_PT_LINESTRIP | BlendFunc::Default.toValue(); + if (state != _lastState) { + render(); + } + _lastState = state; + if (!_vertices.empty()) { + _vertices.reserve(_vertices.size() + 4); + _vertices.push_back(_vertices.back()); + _vertices.back().abgr = 0; + _vertices.push_back(verts[0]); + _vertices.back().abgr = 0; + } else { + _vertices.reserve(_vertices.size() + 2); + } + _vertices.push_back(verts[0]); + _vertices.push_back(verts[1]); +} + void LineRenderer::render() { if (!_vertices.empty()) { bgfx::TransientVertexBuffer vertexBuffer; diff --git a/Source/Node/DrawNode.h b/Source/Node/DrawNode.h index d08334fc8..103aaae43 100644 --- a/Source/Node/DrawNode.h +++ b/Source/Node/DrawNode.h @@ -171,6 +171,8 @@ class LineRenderer : public Renderer { virtual ~LineRenderer() { } virtual void render() override; void push(Line* line); + void pushRect(PosColorVertex verts[4]); + void pushSegment(PosColorVertex verts[2]); protected: LineRenderer(); diff --git a/Source/Node/Node.cpp b/Source/Node/Node.cpp index 8d2d9595f..77cf14049 100644 --- a/Source/Node/Node.cpp +++ b/Source/Node/Node.cpp @@ -23,6 +23,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include "Input/Controller.h" #include "Input/Keyboard.h" #include "Input/TouchDispather.h" +#include "Node/DrawNode.h" #include "Node/Grid.h" #include "Node/Sprite.h" #include "Support/Dictionary.h" @@ -31,7 +32,7 @@ NS_DORA_BEGIN Node::Node(bool unManaged) : _flags( - Node::Visible | Node::SelfVisible | Node::ChildrenVisible | Node::PassOpacity | Node::PassColor3 | Node::TraverseEnabled) + Node::Visible | Node::SelfVisible | Node::ChildrenVisible | Node::PassOpacity | Node::PassColor3 | Node::TraverseEnabled) , _order(0) , _renderOrder(0) , _color() @@ -790,6 +791,45 @@ bool Node::update(double deltaTime) { return result && !isUpdating(); } +void Node::renderDebug() { + if (isShowDebug() && getSize() != Size::zero) { + Matrix transform; + bx::mtxMul(transform, _world, SharedDirector.getViewProjection()); + float w = getWidth(); + float h = getHeight(); + const float anchorSize = 20.0f; + Vec4 positions[] = { + {0, h, 0, 1.0f}, + {w, h, 0, 1.0f}, + {w, 0, 0, 1.0f}, + {0, 0, 0, 1.0f}, + {_anchorPoint.x - anchorSize, _anchorPoint.y, 0, 1.0f}, + {_anchorPoint.x + anchorSize, _anchorPoint.y, 0, 1.0f}, + {_anchorPoint.x, _anchorPoint.y - anchorSize, 0, 1.0f}, + {_anchorPoint.x, _anchorPoint.y + anchorSize, 0, 1.0f}}; + auto color = getColor().toABGR(); + auto themeColor = SharedApplication.getThemeColor(); + themeColor.setOpacity(getRealOpacity()); + auto anchorColor = themeColor.toABGR(); + PosColorVertex verts[8] = { + {0, 0, 0, 0, color}, + {0, 0, 0, 0, color}, + {0, 0, 0, 0, color}, + {0, 0, 0, 0, color}, + {0, 0, 0, 0, anchorColor}, + {0, 0, 0, 0, anchorColor}, + {0, 0, 0, 0, anchorColor}, + {0, 0, 0, 0, anchorColor}}; + for (size_t i = 0; i < 8; i++) { + bx::vec4MulMtx(&verts[i].x, &positions[i].x, transform); + } + SharedRendererManager.setCurrent(SharedLineRenderer.getTarget()); + SharedLineRenderer.pushRect(verts); + SharedLineRenderer.pushSegment(verts + 4); + SharedLineRenderer.pushSegment(verts + 6); + } +} + void Node::visitInner() { if (_flags.isOff(Node::Visible)) { return; @@ -816,8 +856,10 @@ void Node::visitInner() { if (_flags.isOn(Node::SelfVisible)) { if (rendererManager.isGrouping() && _renderOrder != 0) { rendererManager.pushGroupItem(this); - } else + } else { render(); + renderDebug(); + } } /* visit and render child whose order is greater equal than 0 */ @@ -834,8 +876,10 @@ void Node::visitInner() { } else if (_flags.isOn(Node::SelfVisible)) { if (rendererManager.isGrouping() && _renderOrder != 0) { rendererManager.pushGroupItem(this); - } else + } else { render(); + renderDebug(); + } } } @@ -1837,4 +1881,12 @@ void Node::setAsManaged() { } } +void Node::setShowDebug(bool var) { + _flags.set(Node::ShowDebug, var); +} + +bool Node::isShowDebug() const { + return _flags.isOn(Node::ShowDebug); +} + NS_DORA_END diff --git a/Source/Node/Node.h b/Source/Node/Node.h index f88670d3c..bbebe3b05 100644 --- a/Source/Node/Node.h +++ b/Source/Node/Node.h @@ -48,6 +48,7 @@ class Node : public Object { PROPERTY_VIRTUAL_CREF(Vec2, Position); PROPERTY(float, SkewX); PROPERTY(float, SkewY); + PROPERTY_VIRTUAL_BOOL(ShowDebug); PROPERTY_BOOL(Visible); PROPERTY_BOOL(SelfVisible); PROPERTY_BOOL(ChildrenVisible); @@ -292,6 +293,7 @@ class Node : public Object { void resumeActionInList(Action* action); void stopActionInList(Action* action); void handleKeyboardAndController(Event* event); + void renderDebug(); protected: Flag _flags; @@ -358,7 +360,8 @@ class Node : public Object { FixedUpdating = 1 << 19, UnManaged = 1 << 20, InWaitingList = 1 << 21, - UserFlag = 1 << 22, + ShowDebug = 1 << 22, + UserFlag = 1 << 23, }; DORA_TYPE_OVERRIDE(Node); }; diff --git a/Source/Node/Spine.cpp b/Source/Node/Spine.cpp index 33c85b5b5..46282840d 100644 --- a/Source/Node/Spine.cpp +++ b/Source/Node/Spine.cpp @@ -140,10 +140,7 @@ void Spine::setShowDebug(bool var) { _debugLine = nullptr; } } -} - -bool Spine::isShowDebug() const { - return _debugLine != nullptr; + Node::setShowDebug(var); } void Spine::setLook(String name) { diff --git a/Source/Node/Spine.h b/Source/Node/Spine.h index e13903760..7a11d7811 100644 --- a/Source/Node/Spine.h +++ b/Source/Node/Spine.h @@ -22,11 +22,11 @@ class Spine : public Playable { public: PROPERTY_BOOL(DepthWrite); PROPERTY_BOOL(HitTestEnabled); - PROPERTY_BOOL(ShowDebug); virtual bool init() override; virtual bool update(double deltaTime) override; virtual void render() override; virtual void cleanup() override; + virtual void setShowDebug(bool var) override; virtual void setSpeed(float var) override; virtual void setRecovery(float var) override; virtual void setFliped(bool var) override; diff --git a/Source/Physics/PhysicsWorld.cpp b/Source/Physics/PhysicsWorld.cpp index 78e4a373a..547cc0249 100644 --- a/Source/Physics/PhysicsWorld.cpp +++ b/Source/Physics/PhysicsWorld.cpp @@ -202,10 +202,7 @@ void PhysicsWorld::setShowDebug(bool var) { removeChild(_debugDraw->getRenderer()); _debugDraw = nullptr; } -} - -bool PhysicsWorld::isShowDebug() const { - return _debugDraw != nullptr; + Node::setShowDebug(var); } void PhysicsWorld::setIterations(int velocityIter, int positionIter) { diff --git a/Source/Physics/PhysicsWorld.h b/Source/Physics/PhysicsWorld.h index 596193ec7..353a6aa76 100644 --- a/Source/Physics/PhysicsWorld.h +++ b/Source/Physics/PhysicsWorld.h @@ -25,7 +25,7 @@ class PhysicsWorld : public Node { public: virtual ~PhysicsWorld(); PROPERTY_READONLY_REF(pd::World, PrWorld); - PROPERTY_BOOL(ShowDebug); + /** Iterations affect PlayRho`s CPU cost greatly. Lower these values to get better performance, higher values to get better simulation. @@ -38,6 +38,7 @@ class PhysicsWorld : public Node { virtual bool fixedUpdate(double deltaTime) override; virtual bool update(double deltaTime) override; virtual void render() override; + virtual void setShowDebug(bool var) override; void setFixtureData(pr::ShapeID fixture, Sensor* sensor); Sensor* getFixtureData(pr::ShapeID fixture) const; diff --git a/Source/Shader/Draw/fs_draw.bin.h b/Source/Shader/Draw/fs_draw.bin.h index 48f244d0d..ba380ebfa 100644 --- a/Source/Shader/Draw/fs_draw.bin.h +++ b/Source/Shader/Draw/fs_draw.bin.h @@ -1,81 +1,93 @@ // Generated by shaderc, bgfx shader compiler tool, version 1.18.118. -static const uint8_t fs_draw_essl[198] = +static const uint8_t fs_draw_essl[245] = { - 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x00, // FSH............. + 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x00, // FSH............. 0x00, 0x00, 0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, // ..varying highp 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x3b, 0x0a, 0x76, // vec4 v_color0;.v 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, // arying highp vec 0x32, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x3b, 0x0a, 0x76, // 2 v_texcoord0;.v 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, // oid main ().{. - 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x28, // gl_FragColor = ( - 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x20, 0x2a, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, // v_color0 * float - 0x28, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x73, 0x71, // ((. (1.0 - sq - 0x72, 0x74, 0x28, 0x64, 0x6f, 0x74, 0x20, 0x28, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, // rt(dot (v_texcoo - 0x72, 0x64, 0x30, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, // rd0, v_texcoord0 - 0x29, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, // ))). >= 0.0))) - 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, // ;.}... + 0x69, 0x66, 0x20, 0x28, 0x28, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x2e, 0x77, 0x20, // if ((v_color0.w + 0x3d, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, // == 0.0)) {. d + 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x67, // iscard;. };. g + 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x28, 0x76, // l_FragColor = (v + 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x20, 0x2a, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x28, // _color0 * float( + 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x73, 0x71, 0x72, // (. (1.0 - sqr + 0x74, 0x28, 0x64, 0x6f, 0x74, 0x20, 0x28, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, // t(dot (v_texcoor + 0x64, 0x30, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x29, // d0, v_texcoord0) + 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, // )). >= 0.0))); + 0x0a, 0x7d, 0x0a, 0x0a, 0x00, // .}... }; -static const uint8_t fs_draw_glsl[186] = +static const uint8_t fs_draw_glsl[233] = { - 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0x00, // FSH............. + 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x00, // FSH............. 0x00, 0x00, 0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, // ..varying vec4 v 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x3b, 0x0a, 0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, // _color0;.varying 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, // vec2 v_texcoord 0x30, 0x3b, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x28, 0x29, 0x0a, // 0;.void main (). - 0x7b, 0x0a, 0x20, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, // {. gl_FragColor - 0x20, 0x3d, 0x20, 0x28, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x20, 0x2a, 0x20, 0x66, // = (v_color0 * f - 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, // loat((. (1.0 - 0x2d, 0x20, 0x73, 0x71, 0x72, 0x74, 0x28, 0x64, 0x6f, 0x74, 0x20, 0x28, 0x76, 0x5f, 0x74, 0x65, // - sqrt(dot (v_te - 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, // xcoord0, v_texco - 0x6f, 0x72, 0x64, 0x30, 0x29, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x2e, // ord0))). >= 0. - 0x30, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, // 0)));.}... + 0x7b, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, // {. if ((v_color + 0x30, 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, // 0.w == 0.0)) {. + 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x3b, // discard;. }; + 0x0a, 0x20, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, // . gl_FragColor + 0x3d, 0x20, 0x28, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x20, 0x2a, 0x20, 0x66, 0x6c, // = (v_color0 * fl + 0x6f, 0x61, 0x74, 0x28, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, // oat((. (1.0 - + 0x20, 0x73, 0x71, 0x72, 0x74, 0x28, 0x64, 0x6f, 0x74, 0x20, 0x28, 0x76, 0x5f, 0x74, 0x65, 0x78, // sqrt(dot (v_tex + 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x2c, 0x20, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, // coord0, v_texcoo + 0x72, 0x64, 0x30, 0x29, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x3e, 0x3d, 0x20, 0x30, 0x2e, 0x30, // rd0))). >= 0.0 + 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, // )));.}... }; -static const uint8_t fs_draw_spv[634] = +static const uint8_t fs_draw_spv[730] = { - 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x02, // FSH...........d. - 0x00, 0x00, 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x61, 0x00, // ....#.........a. + 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x02, // FSH............. + 0x00, 0x00, 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x6e, 0x00, // ....#.........n. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, // ................ 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, // ......GLSL.std.4 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, // 50.............. 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, // ..............ma - 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3f, 0x00, // in........2...?. + 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x48, 0x00, // in....7...;...H. 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, // ................ 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, // ................ - 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x2e, 0x00, // ..main.......... + 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x37, 0x00, // ..main........7. 0x00, 0x00, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, // ..v_color0...... - 0x05, 0x00, 0x32, 0x00, 0x00, 0x00, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, // ..2...v_texcoord - 0x30, 0x00, 0x05, 0x00, 0x06, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, // 0.....?...bgfx_F - 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2e, 0x00, // ragData0..G..... - 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x32, 0x00, // ..........G...2. - 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x3f, 0x00, // ..........G...?. + 0x05, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, // ..;...v_texcoord + 0x30, 0x00, 0x05, 0x00, 0x06, 0x00, 0x48, 0x00, 0x00, 0x00, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, // 0.....H...bgfx_F + 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x37, 0x00, // ragData0..G...7. + 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x3b, 0x00, // ..........G...;. + 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x48, 0x00, // ..........G...H. 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, // ................ 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, // ..!............. 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x08, 0x00, // ...... ......... 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0e, 0x00, // ................ 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, // ..........+..... - 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, // ..........+..... - 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x20, 0x00, 0x04, 0x00, 0x2a, 0x00, // ..#......? ...*. - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x2a, 0x00, // ..........;...*. - 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x31, 0x00, // .......... ...1. - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x31, 0x00, // ..........;...1. - 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3e, 0x00, // ..2....... ...>. - 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x3e, 0x00, // ..........;...>. - 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, // ..?.......6..... - 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, // ................ - 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2f, 0x00, // ......=......./. - 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x33, 0x00, // ......=.......3. - 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5d, 0x00, // ..2...........]. - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x83, 0x00, // ......B...3..... - 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x5d, 0x00, // ......^...#...]. - 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x01, 0x00, // .........._..... - 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x8e, 0x00, // ..0.......^..... - 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x5f, 0x00, // ......`.../..._. - 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xfd, 0x00, // ..>...?...`..... + 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x26, 0x00, // ..............&. + 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, // ..+.......,..... + 0x80, 0x3f, 0x20, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, // .? ...3......... + 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x33, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x01, 0x00, // ..;...3...7..... + 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, // .. ...:......... + 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x01, 0x00, // ..;...:...;..... + 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, // .. ...G......... + 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x47, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x03, 0x00, // ..;...G...H..... + 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ..6............. + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, // ..............=. + 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x3d, 0x00, // ......8...7...=. + 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x51, 0x00, // ......<...;...Q. + 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x03, 0x00, // ......d...8..... + 0x00, 0x00, 0xb4, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x64, 0x00, // ......&...e...d. + 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, // ..........g..... + 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x67, 0x00, // ......e...f...g. + 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x66, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x01, 0x00, 0xf8, 0x00, // ......f......... + 0x02, 0x00, 0x67, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6a, 0x00, // ..g...........j. + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x83, 0x00, // ......B...<..... + 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x6a, 0x00, // ......k...,...j. + 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x01, 0x00, // ..........l..... + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x8e, 0x00, // ..0.......k..... + 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x6c, 0x00, // ......m...8...l. + 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0xfd, 0x00, // ..>...H...m..... 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // ..8....... }; -static const uint8_t fs_draw_mtl[468] = +static const uint8_t fs_draw_mtl[538] = { - 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x01, // FSH............. + 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, // FSH............. 0x00, 0x00, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x6d, 0x65, 0x74, 0x61, // ..#include .#inclu 0x64, 0x65, 0x20, 0x3c, 0x73, 0x69, 0x6d, 0x64, 0x2f, 0x73, 0x69, 0x6d, 0x64, 0x2e, 0x68, 0x3e, // de @@ -98,19 +110,23 @@ static const uint8_t fs_draw_mtl[468] = 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x5d, 0x5d, 0x29, 0x0a, 0x7b, 0x0a, 0x20, // [stage_in]]).{. 0x20, 0x20, 0x20, 0x78, 0x6c, 0x61, 0x74, 0x4d, 0x74, 0x6c, 0x4d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, // xlatMtlMain_o 0x75, 0x74, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, // ut out = {};. - 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x62, 0x67, 0x66, 0x78, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x61, // out.bgfx_FragDa - 0x74, 0x61, 0x30, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, // ta0 = in.v_color - 0x30, 0x20, 0x2a, 0x20, 0x73, 0x74, 0x65, 0x70, 0x28, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, // 0 * step(0.0, 1. - 0x30, 0x20, 0x2d, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x28, 0x69, 0x6e, 0x2e, 0x76, 0x5f, // 0 - length(in.v_ - 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, // texcoord0));. - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, // return out;.}.. - 0x00, 0x00, 0x00, 0x00, // .... + 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, // if (in.v_color0 + 0x2e, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, // .w == 0.0). { + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, // . discard + 0x5f, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, // _fragment();. + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x62, 0x67, 0x66, 0x78, 0x5f, // }. out.bgfx_ + 0x46, 0x72, 0x61, 0x67, 0x44, 0x61, 0x74, 0x61, 0x30, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x2e, 0x76, // FragData0 = in.v + 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x20, 0x2a, 0x20, 0x73, 0x74, 0x65, 0x70, 0x28, 0x30, // _color0 * step(0 + 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, // .0, 1.0 - length + 0x28, 0x69, 0x6e, 0x2e, 0x76, 0x5f, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x30, 0x29, // (in.v_texcoord0) + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, // );. return ou + 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, // t;.}...... }; -static const uint8_t fs_draw_dx11[462] = +static const uint8_t fs_draw_dx11[502] = { - 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x01, // FSH............. - 0x00, 0x00, 0x44, 0x58, 0x42, 0x43, 0xc5, 0x08, 0x56, 0xa1, 0xa6, 0xf7, 0x60, 0x9c, 0xae, 0xf8, // ..DXBC..V...`... - 0x77, 0x6e, 0xfc, 0x56, 0x6b, 0x13, 0x01, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x03, 0x00, // wn.Vk........... + 0x46, 0x53, 0x48, 0x0b, 0x01, 0x83, 0xf2, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, // FSH............. + 0x00, 0x00, 0x44, 0x58, 0x42, 0x43, 0x91, 0x41, 0x01, 0xce, 0x8e, 0x94, 0xbb, 0xce, 0x74, 0xc0, // ..DXBC.A......t. + 0x53, 0x6c, 0xcf, 0x1a, 0x1a, 0x1d, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x03, 0x00, // Sl.............. 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x49, 0x53, // ..,...........IS 0x47, 0x4e, 0x6c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, // GNl...........P. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // ................ @@ -122,19 +138,22 @@ static const uint8_t fs_draw_dx11[462] = 0x00, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, // ..OSGN,......... 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, // .. ............. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x53, 0x56, 0x5f, 0x54, 0x41, 0x52, // ..........SV_TAR - 0x47, 0x45, 0x54, 0x00, 0xab, 0xab, 0x53, 0x48, 0x44, 0x52, 0xdc, 0x00, 0x00, 0x00, 0x40, 0x00, // GET...SHDR....@. - 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xf2, 0x10, 0x10, 0x00, 0x01, 0x00, // ..7...b......... + 0x47, 0x45, 0x54, 0x00, 0xab, 0xab, 0x53, 0x48, 0x44, 0x52, 0x04, 0x01, 0x00, 0x00, 0x40, 0x00, // GET...SHDR....@. + 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0xf2, 0x10, 0x10, 0x00, 0x01, 0x00, // ..A...b......... 0x00, 0x00, 0x62, 0x10, 0x00, 0x03, 0x32, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x65, 0x00, // ..b...2.......e. 0x00, 0x03, 0xf2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x02, 0x01, 0x00, // ... ......h..... - 0x00, 0x00, 0x0f, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, // ..............F. - 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4b, 0x00, // ......F.......K. - 0x00, 0x05, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, // ................ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, // ................ - 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, // ..A........@.... - 0x80, 0x3f, 0x1d, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, // .?.............. - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, // .......@........ - 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, // ................ - 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x38, 0x00, 0x00, 0x07, 0xf2, 0x20, // ...@.....?8.... - 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1e, // ..............F. - 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // ......>....... + 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x10, // ..............:. + 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, // .......@........ + 0x04, 0x03, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x07, 0x12, 0x00, // ................ + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x10, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x46, 0x10, // ......F.......F. + 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x05, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, // ......K......... + 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x12, 0x00, // ................ + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, // ..........A..... + 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x1d, 0x00, 0x00, 0x07, 0x12, 0x00, // ...@.....?...... + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, // ...............@ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07, 0x12, 0x00, 0x10, 0x00, 0x00, 0x00, // ................ + 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, // ...........@.... + 0x80, 0x3f, 0x38, 0x00, 0x00, 0x07, 0xf2, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, // .?8.... ........ + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x1e, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, // ......F.......>. + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // ...... }; diff --git a/Source/Shader/Draw/fs_draw.sc b/Source/Shader/Draw/fs_draw.sc index 547562193..a8c0bc06a 100644 --- a/Source/Shader/Draw/fs_draw.sc +++ b/Source/Shader/Draw/fs_draw.sc @@ -4,9 +4,10 @@ $input v_color0, v_texcoord0 void main() { -#if defined GL_OES_standard_derivatives - gl_FragColor = v_color0*smoothstep(0.0, length(fwidth(v_texcoord0)), 1.0 - length(v_texcoord0)); + if (v_color0.a == 0.0) discard; +#if defined GL_OES_standard_derivatives + gl_FragColor = v_color0 * smoothstep(0.0, length(fwidth(v_texcoord0)), 1.0 - length(v_texcoord0)); #else - gl_FragColor = v_color0*step(0.0, 1.0 - length(v_texcoord0)); + gl_FragColor = v_color0 * step(0.0, 1.0 - length(v_texcoord0)); #endif } diff --git a/Source/Wasm/Dora/AlignNodeWasm.hpp b/Source/Wasm/Dora/AlignNodeWasm.hpp new file mode 100644 index 000000000..707b3ec70 --- /dev/null +++ b/Source/Wasm/Dora/AlignNodeWasm.hpp @@ -0,0 +1,22 @@ +/* Copyright (c) 2024 Li Jin, dragon-fly@qq.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +static int32_t alignnode_type() { + return DoraType(); +} +static void alignnode_css(int64_t self, int64_t style) { + r_cast(self)->css(*str_from(style)); +} +static int64_t alignnode_new(int32_t is_window_root) { + return from_object(AlignNode::create(is_window_root != 0)); +} +static void linkAlignNode(wasm3::module3& mod) { + mod.link_optional("*", "alignnode_type", alignnode_type); + mod.link_optional("*", "alignnode_css", alignnode_css); + mod.link_optional("*", "alignnode_new", alignnode_new); +} \ No newline at end of file diff --git a/Source/Wasm/Dora/DragonBoneWasm.hpp b/Source/Wasm/Dora/DragonBoneWasm.hpp index d74d0c133..bdf585589 100644 --- a/Source/Wasm/Dora/DragonBoneWasm.hpp +++ b/Source/Wasm/Dora/DragonBoneWasm.hpp @@ -9,12 +9,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI static int32_t dragonbone_type() { return DoraType(); } -static void dragonbone_set_show_debug(int64_t self, int32_t var) { - r_cast(self)->setShowDebug(var != 0); -} -static int32_t dragonbone_is_show_debug(int64_t self) { - return r_cast(self)->isShowDebug() ? 1 : 0; -} static void dragonbone_set_hit_test_enabled(int64_t self, int32_t var) { r_cast(self)->setHitTestEnabled(var != 0); } @@ -41,8 +35,6 @@ static int64_t dragonbone_get_animations(int64_t bone_str) { } static void linkDragonBone(wasm3::module3& mod) { mod.link_optional("*", "dragonbone_type", dragonbone_type); - mod.link_optional("*", "dragonbone_set_show_debug", dragonbone_set_show_debug); - mod.link_optional("*", "dragonbone_is_show_debug", dragonbone_is_show_debug); mod.link_optional("*", "dragonbone_set_hit_test_enabled", dragonbone_set_hit_test_enabled); mod.link_optional("*", "dragonbone_is_hit_test_enabled", dragonbone_is_hit_test_enabled); mod.link_optional("*", "dragonbone_contains_point", dragonbone_contains_point); diff --git a/Source/Wasm/Dora/NodeWasm.hpp b/Source/Wasm/Dora/NodeWasm.hpp index 59d6af593..16d5b9a5d 100644 --- a/Source/Wasm/Dora/NodeWasm.hpp +++ b/Source/Wasm/Dora/NodeWasm.hpp @@ -216,6 +216,12 @@ static void node_set_render_group(int64_t self, int32_t var) { static int32_t node_is_render_group(int64_t self) { return r_cast(self)->isRenderGroup() ? 1 : 0; } +static void node_set_show_debug(int64_t self, int32_t var) { + r_cast(self)->setShowDebug(var != 0); +} +static int32_t node_is_show_debug(int64_t self) { + return r_cast(self)->isShowDebug() ? 1 : 0; +} static void node_set_render_order(int64_t self, int32_t var) { r_cast(self)->setRenderOrder(s_cast(var)); } @@ -482,6 +488,8 @@ static void linkNode(wasm3::module3& mod) { mod.link_optional("*", "node_is_controller_enabled", node_is_controller_enabled); mod.link_optional("*", "node_set_render_group", node_set_render_group); mod.link_optional("*", "node_is_render_group", node_is_render_group); + mod.link_optional("*", "node_set_show_debug", node_set_show_debug); + mod.link_optional("*", "node_is_show_debug", node_is_show_debug); mod.link_optional("*", "node_set_render_order", node_set_render_order); mod.link_optional("*", "node_get_render_order", node_get_render_order); mod.link_optional("*", "node_add_child_with_order_tag", node_add_child_with_order_tag); diff --git a/Source/Wasm/Dora/PhysicsWorldWasm.hpp b/Source/Wasm/Dora/PhysicsWorldWasm.hpp index 0261e54b9..2c83ba01e 100644 --- a/Source/Wasm/Dora/PhysicsWorldWasm.hpp +++ b/Source/Wasm/Dora/PhysicsWorldWasm.hpp @@ -9,12 +9,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI static int32_t physicsworld_type() { return DoraType(); } -static void physicsworld_set_show_debug(int64_t self, int32_t var) { - r_cast(self)->setShowDebug(var != 0); -} -static int32_t physicsworld_is_show_debug(int64_t self) { - return r_cast(self)->isShowDebug() ? 1 : 0; -} static int32_t physicsworld_query(int64_t self, int64_t rect, int32_t func, int64_t stack) { std::shared_ptr deref(nullptr, [func](auto) { SharedWasmRuntime.deref(func); @@ -61,8 +55,6 @@ static int64_t physicsworld_new() { } static void linkPhysicsWorld(wasm3::module3& mod) { mod.link_optional("*", "physicsworld_type", physicsworld_type); - mod.link_optional("*", "physicsworld_set_show_debug", physicsworld_set_show_debug); - mod.link_optional("*", "physicsworld_is_show_debug", physicsworld_is_show_debug); mod.link_optional("*", "physicsworld_query", physicsworld_query); mod.link_optional("*", "physicsworld_raycast", physicsworld_raycast); mod.link_optional("*", "physicsworld_set_iterations", physicsworld_set_iterations); diff --git a/Source/Wasm/Dora/SpineWasm.hpp b/Source/Wasm/Dora/SpineWasm.hpp index c10a640da..a8bbd45bf 100644 --- a/Source/Wasm/Dora/SpineWasm.hpp +++ b/Source/Wasm/Dora/SpineWasm.hpp @@ -9,12 +9,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI static int32_t spine_type() { return DoraType(); } -static void spine_set_show_debug(int64_t self, int32_t var) { - r_cast(self)->setShowDebug(var != 0); -} -static int32_t spine_is_show_debug(int64_t self) { - return r_cast(self)->isShowDebug() ? 1 : 0; -} static void spine_set_hit_test_enabled(int64_t self, int32_t var) { r_cast(self)->setHitTestEnabled(var != 0); } @@ -44,8 +38,6 @@ static int64_t spine_get_animations(int64_t spine_str) { } static void linkSpine(wasm3::module3& mod) { mod.link_optional("*", "spine_type", spine_type); - mod.link_optional("*", "spine_set_show_debug", spine_set_show_debug); - mod.link_optional("*", "spine_is_show_debug", spine_is_show_debug); mod.link_optional("*", "spine_set_hit_test_enabled", spine_set_hit_test_enabled); mod.link_optional("*", "spine_is_hit_test_enabled", spine_is_hit_test_enabled); mod.link_optional("*", "spine_set_bone_rotation", spine_set_bone_rotation); diff --git a/Source/Wasm/WasmRuntime.cpp b/Source/Wasm/WasmRuntime.cpp index 6b6c0bc29..29abc2238 100644 --- a/Source/Wasm/WasmRuntime.cpp +++ b/Source/Wasm/WasmRuntime.cpp @@ -1327,6 +1327,7 @@ static void db_do_exec_async(String sql, DBParams& params, const std::function dora.Model.Type | null; function handleDragonBoneAttribute(this: void, cnode: dora.DragonBone.Type, enode: React.Element, k: any, v: any) { switch (k as keyof JSX.DragonBone) { - case 'showDebug': cnode.showDebug = v; return true; case 'hitTestEnabled': cnode.hitTestEnabled = true; return true; } return handlePlayableAttribute(cnode, enode, k, v); @@ -257,7 +256,6 @@ let getModel: (this: void, enode: React.Element) => dora.Model.Type | null; function handleSpineAttribute(this: void, cnode: dora.Spine.Type, enode: React.Element, k: any, v: any) { switch (k as keyof JSX.Spine) { - case 'showDebug': cnode.showDebug = v; return true; case 'hitTestEnabled': cnode.hitTestEnabled = true; return true; } return handlePlayableAttribute(cnode, enode, k, v); @@ -503,20 +501,11 @@ let getMenu: (this: void, enode: React.Element) => dora.Menu.Type; }; } -let getPhysicsWorld: (this: void, enode: React.Element) => dora.PhysicsWorld.Type; -{ - function handlePhysicsWorldAttribute(this: void, cnode: dora.PhysicsWorld.Type, _enode: React.Element, k: any, v: any) { - switch (k as keyof JSX.PhysicsWorld) { - case 'showDebug': cnode.showDebug = v; return true; - } - return false; - } - getPhysicsWorld = (enode: React.Element) => { - const node = dora.PhysicsWorld(); - const cnode = getNode(enode, node, handlePhysicsWorldAttribute); - return cnode as dora.PhysicsWorld.Type; - }; -} +function getPhysicsWorld(this: void, enode: React.Element) { + const node = dora.PhysicsWorld(); + const cnode = getNode(enode, node); + return cnode as dora.PhysicsWorld.Type; +}; let getBody: (this: void, enode: React.Element, world: dora.PhysicsWorld.Type) => dora.Body.Type; { @@ -689,6 +678,51 @@ let getCustomNode: (this: void, enode: React.Element) => dora.Node.Type | null; }; } +let getAlignNode: (this: void, enode: React.Element) => dora.AlignNode.Type; +{ + function handleAlignNode(this: void, _cnode: dora.AlignNode.Type, _enode: React.Element, k: any, _v: any) { + switch (k as keyof JSX.AlignNode) { + case 'windowRoot': return true; + case 'style': return true; + case 'onLayout': return true; + } + return false; + } + getAlignNode = (enode: React.Element) => { + const alignNode = enode.props as JSX.AlignNode; + const node = dora.AlignNode(alignNode.windowRoot); + if (alignNode.style) { + const items: string[] = []; + for (let [k, v] of pairs(alignNode.style)) { + let [name] = string.gsub(k, "%u", "-%1"); + name = name.toLowerCase(); + switch (k) { + case 'margin': case 'padding': + case 'border': case 'gap': { + if (type(v) === 'table') { + const valueStr = table.concat((v as any[]).map(item => tostring(item)), ',') + items.push(`${name}:${valueStr}`); + } else { + items.push(`${name}:${v}`); + } + break; + } + default: + items.push(`${name}:${v}`); + break; + } + } + if (alignNode.onLayout) { + node.slot(dora.Slot.AlignLayout, alignNode.onLayout); + } + const styleStr = table.concat(items, ';'); + node.css(styleStr); + } + const cnode = getNode(enode, node, handleAlignNode); + return cnode as dora.AlignNode.Type; + }; +} + function addChild(this: void, nodeStack: dora.Node.Type[], cnode: dora.Node.Type, enode: React.Element) { if (nodeStack.length > 0) { const last = nodeStack[nodeStack.length - 1]; @@ -1238,13 +1272,16 @@ const elementMap: ElementMap = { joint.damping ?? 0.7 ); }, - 'custom-node': (nodeStack: dora.Node.Type[], enode: React.Element, parent?: React.Element) => { + 'custom-node': (nodeStack: dora.Node.Type[], enode: React.Element, _parent?: React.Element) => { const node = getCustomNode(enode); if (node !== null) { addChild(nodeStack, node, enode); } }, 'custom-element': () => {}, + 'align-node': (nodeStack: dora.Node.Type[], enode: React.Element, _parent?: React.Element) => { + addChild(nodeStack, getAlignNode(enode), enode); + }, } function visitNode(this: void, nodeStack: dora.Node.Type[], node: React.Element | React.Element[], parent?: React.Element) { if (type(node) !== "table") { diff --git a/Tools/dora-rust/dora-test/Cargo.lock b/Tools/dora-rust/dora-test/Cargo.lock index 23fcf3d17..23691486a 100644 --- a/Tools/dora-rust/dora-test/Cargo.lock +++ b/Tools/dora-rust/dora-test/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "dora-ssr" -version = "0.2.4" +version = "0.2.5" dependencies = [ "enumflags2", "once_cell", diff --git a/Tools/dora-rust/dora-test/src/main.rs b/Tools/dora-rust/dora-test/src/main.rs index 28b1ca6cc..55e85e759 100644 --- a/Tools/dora-rust/dora-test/src/main.rs +++ b/Tools/dora-rust/dora-test/src/main.rs @@ -57,6 +57,10 @@ fn main() { all_clear(); tests::excel_test::test(); } + if ImGui::button("Layout", &button_size) { + all_clear(); + tests::layout::test(); + } }); co.waiter().await; } diff --git a/Tools/dora-rust/dora-test/src/tests.rs b/Tools/dora-rust/dora-test/src/tests.rs index 6ddfce169..844787f03 100644 --- a/Tools/dora-rust/dora-test/src/tests.rs +++ b/Tools/dora-rust/dora-test/src/tests.rs @@ -7,4 +7,5 @@ pub mod model; pub mod sprite; pub mod rander_target; pub mod excel_test; -pub mod quick_start; \ No newline at end of file +pub mod quick_start; +pub mod layout; \ No newline at end of file diff --git a/Tools/dora-rust/dora-test/src/tests/layout.rs b/Tools/dora-rust/dora-test/src/tests/layout.rs new file mode 100644 index 000000000..e87001458 --- /dev/null +++ b/Tools/dora-rust/dora-test/src/tests/layout.rs @@ -0,0 +1,51 @@ +use dora_ssr::*; + +/* +const root = AlignNode(true); +root.showDebug = true; + +const node1 = AlignNode(); +node1.css(` + height: 250; + margin: 10; + padding: 10; + align-items: flex-start; + flex-wrap: wrap; +`); +node1.showDebug = true; +node1.addTo(root); + +for (let _ of $range(1, 10)) { + const node = AlignNode(); + node.css(`margin: 5; height: 50; width: 50;`); + node.showDebug = true; + node.addTo(node1); +} +*/ + +pub fn test() { + let mut root = AlignNode::new(true); + root.set_show_debug(true); + + let mut node1 = AlignNode::new(false); + node1.css(r#" + height: 250; + margin: 10; + padding: 10; + align-items: flex-start; + flex-wrap: wrap; + "#); + node1.set_show_debug(true); + node1.add_to(&root); + + for _ in 1..=10 { + let mut node = AlignNode::new(false); + node.css(r#" + margin: 5; + height: 50; + width: 50; + "#); + node.set_show_debug(true); + node.add_to(&node1); + } +} \ No newline at end of file diff --git a/Tools/dora-rust/dora/Cargo.lock b/Tools/dora-rust/dora/Cargo.lock index 2f1d9afc6..fc50a7b38 100644 --- a/Tools/dora-rust/dora/Cargo.lock +++ b/Tools/dora-rust/dora/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "dora-ssr" -version = "0.2.4" +version = "0.2.5" dependencies = [ "enumflags2", "once_cell", diff --git a/Tools/dora-rust/dora/Cargo.toml b/Tools/dora-rust/dora/Cargo.toml index 3d0657c71..fcea689a9 100644 --- a/Tools/dora-rust/dora/Cargo.toml +++ b/Tools/dora-rust/dora/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dora-ssr" -version = "0.2.4" +version = "0.2.5" authors = ["Li Jin "] edition = "2021" license = "MIT" diff --git a/Tools/dora-rust/dora/src/dora.rs b/Tools/dora-rust/dora/src/dora.rs index 4639a0654..b8017a737 100644 --- a/Tools/dora-rust/dora/src/dora.rs +++ b/Tools/dora-rust/dora/src/dora.rs @@ -86,6 +86,8 @@ mod spine; pub use spine::Spine; mod dragon_bone; pub use dragon_bone::DragonBone; +mod align_node; +pub use align_node::AlignNode; mod physics_world; pub use physics_world::{IPhysicsWorld, PhysicsWorld}; mod fixture_def; @@ -176,6 +178,7 @@ static OBJECT_MAP: Lazy Option>>> = Lazy::new(|| Model::type_info(), Spine::type_info(), DragonBone::type_info(), + AlignNode::type_info(), PhysicsWorld::type_info(), FixtureDef::type_info(), BodyDef::type_info(), diff --git a/Tools/dora-rust/dora/src/dora/align_node.rs b/Tools/dora-rust/dora/src/dora/align_node.rs new file mode 100644 index 000000000..7190c3607 --- /dev/null +++ b/Tools/dora-rust/dora/src/dora/align_node.rs @@ -0,0 +1,70 @@ +/* Copyright (c) 2024 Li Jin, dragon-fly@qq.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +extern "C" { + fn alignnode_type() -> i32; + fn alignnode_css(slf: i64, style: i64); + fn alignnode_new(is_window_root: i32) -> i64; +} +use crate::dora::IObject; +use crate::dora::INode; +impl INode for AlignNode { } +/// A node used for aligning layout elements. +pub struct AlignNode { raw: i64 } +crate::dora_object!(AlignNode); +impl AlignNode { + pub(crate) fn type_info() -> (i32, fn(i64) -> Option>) { + (unsafe { alignnode_type() }, |raw: i64| -> Option> { + match raw { + 0 => None, + _ => Some(Box::new(AlignNode { raw: raw })) + } + }) + } + /// Sets the layout style of the node. + /// + /// # Arguments + /// + /// * `style` - The layout style to set. + /// The following properties can be set through a CSS style string: + /// ## Layout direction and alignment + /// * direction: Sets the direction (ltr, rtl, inherit). + /// * align-items, align-self, align-content: Sets the alignment of different items (flex-start, center, stretch, flex-end, auto). + /// * flex-direction: Sets the layout direction (column, row, column-reverse, row-reverse). + /// * justify-content: Sets the arrangement of child items (flex-start, center, flex-end, space-between, space-around, space-evenly). + /// ## Flex properties + /// * flex: Sets the overall size of the flex container. + /// * flex-grow: Sets the flex growth value. + /// * flex-shrink: Sets the flex shrink value. + /// * flex-wrap: Sets whether to wrap (nowrap, wrap, wrap-reverse). + /// * flex-basis: Sets the flex basis value or percentage. + /// ## Margins and dimensions + /// * margin: Can be set by a single value or multiple values separated by commas, percentages or auto for each side. + /// * margin-top, margin-right, margin-bottom, margin-left, margin-start, margin-end: Sets the margin values, percentages or auto. + /// * padding: Can be set by a single value or multiple values separated by commas or percentages for each side. + /// * padding-top, padding-right, padding-bottom, padding-left: Sets the padding values or percentages. + /// * border: Can be set by a single value or multiple values separated by commas for each side. + /// * width, height, min-width, min-height, max-width, max-height: Sets the dimension values or percentage properties. + /// ## Positioning + /// * top, right, bottom, left, start, end, horizontal, vertical: Sets the positioning property values or percentages. + /// ## Other properties + /// * position: Sets the positioning type (absolute, relative, static). + /// * overflow: Sets the overflow property (visible, hidden, scroll). + /// * display: Controls whether to display (flex, none). + pub fn css(&mut self, style: &str) { + unsafe { alignnode_css(self.raw(), crate::dora::from_string(style)); } + } + /// Creates a new AlignNode object. + /// + /// # Arguments + /// + /// * `isWindowRoot` - Whether the node is a window root node. A window root node will automatically listen for window size change events and update the layout accordingly. + pub fn new(is_window_root: bool) -> AlignNode { + unsafe { return AlignNode { raw: alignnode_new(if is_window_root { 1 } else { 0 }) }; } + } +} \ No newline at end of file diff --git a/Tools/dora-rust/dora/src/dora/dragon_bone.rs b/Tools/dora-rust/dora/src/dora/dragon_bone.rs index cc2769106..0fd7056c6 100644 --- a/Tools/dora-rust/dora/src/dora/dragon_bone.rs +++ b/Tools/dora-rust/dora/src/dora/dragon_bone.rs @@ -8,8 +8,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI extern "C" { fn dragonbone_type() -> i32; - fn dragonbone_set_show_debug(slf: i64, var: i32); - fn dragonbone_is_show_debug(slf: i64) -> i32; fn dragonbone_set_hit_test_enabled(slf: i64, var: i32); fn dragonbone_is_hit_test_enabled(slf: i64) -> i32; fn dragonbone_contains_point(slf: i64, x: f32, y: f32) -> i64; @@ -36,14 +34,6 @@ impl DragonBone { } }) } - /// Sets whether to show debug graphics. - pub fn set_show_debug(&mut self, var: bool) { - unsafe { dragonbone_set_show_debug(self.raw(), if var { 1 } else { 0 }) }; - } - /// Gets whether to show debug graphics. - pub fn is_show_debug(&self) -> bool { - return unsafe { dragonbone_is_show_debug(self.raw()) != 0 }; - } /// Sets whether hit testing is enabled. pub fn set_hit_test_enabled(&mut self, var: bool) { unsafe { dragonbone_set_hit_test_enabled(self.raw(), if var { 1 } else { 0 }) }; diff --git a/Tools/dora-rust/dora/src/dora/node.rs b/Tools/dora-rust/dora/src/dora/node.rs index fd6195892..b9e5230e4 100644 --- a/Tools/dora-rust/dora/src/dora/node.rs +++ b/Tools/dora-rust/dora/src/dora/node.rs @@ -77,6 +77,8 @@ extern "C" { fn node_is_controller_enabled(slf: i64) -> i32; fn node_set_render_group(slf: i64, var: i32); fn node_is_render_group(slf: i64) -> i32; + fn node_set_show_debug(slf: i64, var: i32); + fn node_is_show_debug(slf: i64) -> i32; fn node_set_render_order(slf: i64, var: i32); fn node_get_render_order(slf: i64) -> i32; fn node_add_child_with_order_tag(slf: i64, child: i64, order: i32, tag: i64); @@ -405,6 +407,14 @@ pub trait INode: IObject { fn is_render_group(&self) -> bool { return unsafe { node_is_render_group(self.raw()) != 0 }; } + /// Sets whether debug graphic should be displayed for the node. + fn set_show_debug(&mut self, var: bool) { + unsafe { node_set_show_debug(self.raw(), if var { 1 } else { 0 }) }; + } + /// Gets whether debug graphic should be displayed for the node. + fn is_show_debug(&self) -> bool { + return unsafe { node_is_show_debug(self.raw()) != 0 }; + } /// Sets the rendering order number for group rendering. Nodes with lower rendering orders are rendered earlier. fn set_render_order(&mut self, var: i32) { unsafe { node_set_render_order(self.raw(), var) }; diff --git a/Tools/dora-rust/dora/src/dora/physics_world.rs b/Tools/dora-rust/dora/src/dora/physics_world.rs index eda79b9b5..d2a468a21 100644 --- a/Tools/dora-rust/dora/src/dora/physics_world.rs +++ b/Tools/dora-rust/dora/src/dora/physics_world.rs @@ -8,8 +8,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI extern "C" { fn physicsworld_type() -> i32; - fn physicsworld_set_show_debug(slf: i64, var: i32); - fn physicsworld_is_show_debug(slf: i64) -> i32; fn physicsworld_query(slf: i64, rect: i64, func: i32, stack: i64) -> i32; fn physicsworld_raycast(slf: i64, start: i64, stop: i64, closest: i32, func: i32, stack: i64) -> i32; fn physicsworld_set_iterations(slf: i64, velocity_iter: i32, position_iter: i32); @@ -27,14 +25,6 @@ pub struct PhysicsWorld { raw: i64 } crate::dora_object!(PhysicsWorld); impl IPhysicsWorld for PhysicsWorld { } pub trait IPhysicsWorld: INode { - /// Sets whether debug graphic should be displayed for the physics world. - fn set_show_debug(&mut self, var: bool) { - unsafe { physicsworld_set_show_debug(self.raw(), if var { 1 } else { 0 }) }; - } - /// Gets whether debug graphic should be displayed for the physics world. - fn is_show_debug(&self) -> bool { - return unsafe { physicsworld_is_show_debug(self.raw()) != 0 }; - } /// Queries the physics world for all bodies that intersect with the specified rectangle. /// /// # Arguments diff --git a/Tools/dora-rust/dora/src/dora/spine.rs b/Tools/dora-rust/dora/src/dora/spine.rs index da04c300a..c33b8d86e 100644 --- a/Tools/dora-rust/dora/src/dora/spine.rs +++ b/Tools/dora-rust/dora/src/dora/spine.rs @@ -8,8 +8,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI extern "C" { fn spine_type() -> i32; - fn spine_set_show_debug(slf: i64, var: i32); - fn spine_is_show_debug(slf: i64) -> i32; fn spine_set_hit_test_enabled(slf: i64, var: i32); fn spine_is_hit_test_enabled(slf: i64) -> i32; fn spine_set_bone_rotation(slf: i64, name: i64, rotation: f32) -> i32; @@ -37,14 +35,6 @@ impl Spine { } }) } - /// Sets whether to show debug graphics. - pub fn set_show_debug(&mut self, var: bool) { - unsafe { spine_set_show_debug(self.raw(), if var { 1 } else { 0 }) }; - } - /// Gets whether to show debug graphics. - pub fn is_show_debug(&self) -> bool { - return unsafe { spine_is_show_debug(self.raw()) != 0 }; - } /// Sets whether hit testing is enabled. pub fn set_hit_test_enabled(&mut self, var: bool) { unsafe { spine_set_hit_test_enabled(self.raw(), if var { 1 } else { 0 }) }; diff --git a/Tools/tolua++/Dora.h b/Tools/tolua++/Dora.h index 1b8b0c8ac..0b9ee358f 100644 --- a/Tools/tolua++/Dora.h +++ b/Tools/tolua++/Dora.h @@ -356,6 +356,7 @@ class Node : public Object tolua_property__bool bool keyboardEnabled; tolua_property__bool bool controllerEnabled; tolua_property__bool bool renderGroup; + tolua_property__bool bool showDebug; tolua_property__common int renderOrder; void addChild(Node* child, int order, String tag); @@ -665,7 +666,6 @@ class Model : public Playable class Spine : public Playable { - tolua_property__bool bool showDebug; tolua_property__bool bool hitTestEnabled; bool setBoneRotation(String name, float rotation); static Spine* create(String skelFile, String atlasFile); @@ -676,7 +676,6 @@ class Spine : public Playable class DragonBone : public Playable { - tolua_property__bool bool showDebug; tolua_property__bool bool hitTestEnabled; static DragonBone* create(String boneFile, String atlasFile); static DragonBone* create(String boneStr); @@ -684,9 +683,14 @@ class DragonBone : public Playable static tolua_outside void DragonBone_getAnimationNames @ getAnimations(String boneStr); }; +class AlignNode : public Node +{ + void css(String css); + static AlignNode* create(bool isRoot = false); +}; + class PhysicsWorld : public Node { - tolua_property__bool bool showDebug; bool query(Rect rect, tolua_function_bool handler); bool raycast(Vec2 start, Vec2 stop, bool closest, tolua_function_bool handler); void setIterations(int velocityIter, int positionIter); diff --git a/Tools/tolua++/basic.lua b/Tools/tolua++/basic.lua index 9e85ff66a..529b1ebbe 100644 --- a/Tools/tolua++/basic.lua +++ b/Tools/tolua++/basic.lua @@ -104,6 +104,7 @@ local objects = { "Label", "ML::QLearner", "SVGDef", + "AlignNode", "Platformer::Unit", "Platformer::Face", "Platformer::PlatformCamera",