first commit
This commit is contained in:
commit
5bf207ee08
27 changed files with 3682 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.fish
|
||||
/target
|
672
Cargo.lock
generated
Normal file
672
Cargo.lock
generated
Normal file
|
@ -0,0 +1,672 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ab_glyph_rasterizer"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-url"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imagesize"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
|
||||
|
||||
[[package]]
|
||||
name = "kurbo"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "makepad-android-state"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd004cda8be459fd76954218b76a1249a079fb9360bbca4e724cb7ddb2962857"
|
||||
dependencies = [
|
||||
"makepad-jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-derive-live"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-live-id",
|
||||
"makepad-micro-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-derive-wasm-bridge"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-micro-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-derive-widget"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-live-id",
|
||||
"makepad-micro-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-draw"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"ab_glyph_rasterizer",
|
||||
"fxhash",
|
||||
"makepad-html",
|
||||
"makepad-platform",
|
||||
"makepad-rustybuzz",
|
||||
"makepad-vector",
|
||||
"sdfer",
|
||||
"unicode-bidi",
|
||||
"unicode-linebreak",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-futures"
|
||||
version = "0.4.0"
|
||||
|
||||
[[package]]
|
||||
name = "makepad-futures-legacy"
|
||||
version = "0.7.0"
|
||||
|
||||
[[package]]
|
||||
name = "makepad-html"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-live-id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-http"
|
||||
version = "0.4.0"
|
||||
|
||||
[[package]]
|
||||
name = "makepad-jni-sys"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9775cbec5fa0647500c3e5de7c850280a88335d1d2d770e5aa2332b801ba7064"
|
||||
|
||||
[[package]]
|
||||
name = "makepad-live-compiler"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"makepad-derive-live",
|
||||
"makepad-live-tokenizer",
|
||||
"makepad-math",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-live-id"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-live-id-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-live-id-macros"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-micro-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-live-tokenizer"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-live-id",
|
||||
"makepad-math",
|
||||
"makepad-micro-serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-markdown"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-live-id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-math"
|
||||
version = "0.4.0"
|
||||
|
||||
[[package]]
|
||||
name = "makepad-micro-proc-macro"
|
||||
version = "0.4.0"
|
||||
|
||||
[[package]]
|
||||
name = "makepad-micro-serde"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-micro-serde-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-micro-serde-derive"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-micro-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-objc-sys"
|
||||
version = "0.4.0"
|
||||
|
||||
[[package]]
|
||||
name = "makepad-platform"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"makepad-android-state",
|
||||
"makepad-futures",
|
||||
"makepad-futures-legacy",
|
||||
"makepad-http",
|
||||
"makepad-jni-sys",
|
||||
"makepad-objc-sys",
|
||||
"makepad-shader-compiler",
|
||||
"makepad-wasm-bridge",
|
||||
"makepad-windows",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-rustybuzz"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bytemuck",
|
||||
"smallvec",
|
||||
"ttf-parser",
|
||||
"unicode-bidi-mirroring",
|
||||
"unicode-ccc",
|
||||
"unicode-properties",
|
||||
"unicode-script",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-shader-compiler"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"makepad-live-compiler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-vector"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"resvg",
|
||||
"ttf-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-wasm-bridge"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"makepad-derive-wasm-bridge",
|
||||
"makepad-live-id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-widgets"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"makepad-derive-widget",
|
||||
"makepad-draw",
|
||||
"makepad-html",
|
||||
"makepad-markdown",
|
||||
"makepad-zune-jpeg",
|
||||
"makepad-zune-png",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-windows"
|
||||
version = "0.51.1"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-zune-core"
|
||||
version = "0.2.14"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-zune-inflate"
|
||||
version = "0.2.54"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-zune-jpeg"
|
||||
version = "0.3.17"
|
||||
dependencies = [
|
||||
"makepad-zune-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "makepad-zune-png"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"makepad-zune-core",
|
||||
"makepad-zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mumu"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"makepad-widgets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resvg"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pico-args",
|
||||
"rgb",
|
||||
"svgtypes",
|
||||
"tiny-skia",
|
||||
"usvg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||
|
||||
[[package]]
|
||||
name = "sdfer"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27fd75ebc7c721a70d202c7cdd2beb108bbe5dfaaea68e06aff4de2f4cc240ed"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "simplecss"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "strict-num"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||
dependencies = [
|
||||
"float-cmp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "svgtypes"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e"
|
||||
dependencies = [
|
||||
"kurbo",
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-skia"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"png",
|
||||
"tiny-skia-path",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-skia-path"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"bytemuck",
|
||||
"strict-num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.21.1"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi-mirroring"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ccc"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-properties"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-script"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "usvg"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"data-url",
|
||||
"flate2",
|
||||
"imagesize",
|
||||
"kurbo",
|
||||
"log",
|
||||
"pico-args",
|
||||
"roxmltree",
|
||||
"simplecss",
|
||||
"siphasher",
|
||||
"strict-num",
|
||||
"svgtypes",
|
||||
"tiny-skia-path",
|
||||
"xmlwriter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.51.1"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "xmlwriter"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "mumu"
|
||||
version = "0.1.0"
|
||||
authors = ["Stijn Kuipers <stijnkuipers@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "BigFish"
|
||||
license = "MIT OR Apache-2.0"
|
||||
metadata.makepad-auto-version = "zqpv-Yj-K7WNVK2I8h5Okhho46Q="
|
||||
|
||||
[dependencies]
|
||||
makepad-widgets = { path = "../makepad/widgets", version = "0.6.0" }
|
0
README.md
Normal file
0
README.md
Normal file
BIN
resources/colourfish.png
Normal file
BIN
resources/colourfish.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
BIN
resources/tinrs_mobile.png
Normal file
BIN
resources/tinrs_mobile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
82
src/app.rs
Normal file
82
src/app.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use crate::editor::fish_doc::*;
|
||||
use makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import crate::editor::fish_patch_editor::*;
|
||||
import crate::editor::fish_block_editor::*;
|
||||
import crate::app_web::AppWebUI;
|
||||
// import crate::homescreen::BigFishHomeScreen;
|
||||
import crate::editor::fish_theme::*;
|
||||
// import crate::editor::fish_connection_widget::*;
|
||||
import crate::editor::fish_selector_widget::*;
|
||||
// import crate::lua_console::*;
|
||||
|
||||
App = {{App}} {
|
||||
|
||||
ui: <Window> {
|
||||
|
||||
show_bg: true
|
||||
width: Fill,
|
||||
height: Fill,
|
||||
padding: 0.,
|
||||
margin: 0.,
|
||||
draw_bg: {
|
||||
fn pixel(self) -> vec4 {
|
||||
let Pos = floor(self.pos*self.rect_size *0.10);
|
||||
let PatternMask = mod(Pos.x + mod(Pos.y, 2.0), 2.0);
|
||||
return vec4(0.03,0.03,0.03,1);
|
||||
}
|
||||
}
|
||||
|
||||
caption_bar = {
|
||||
visible: true,
|
||||
caption_label = {label ={text: "TiNRS BigFish" }}
|
||||
};
|
||||
|
||||
window_menu = {
|
||||
main = Main {items: [app, file]}
|
||||
app = Sub {name: "TiNRS BigFish", items: [about, line, settings, line, quit]}
|
||||
file = Sub {name: "File", items: [newfile]}
|
||||
about = Item {name: "About BigFish", enabled: true}
|
||||
settings = Item {name: "Settings", enabled: true}
|
||||
quit = Item {name: "Quit BigFish", key: KeyQ}
|
||||
newfile = Item {name: "New", key: KeyN}
|
||||
line = Line,
|
||||
}
|
||||
|
||||
body = <AppWebUI>{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app_main!(App);
|
||||
|
||||
#[derive(Live, LiveHook)]
|
||||
pub struct App {
|
||||
#[live]
|
||||
ui: WidgetRef,
|
||||
}
|
||||
|
||||
impl LiveRegister for App {
|
||||
fn live_register(cx: &mut Cx) {
|
||||
crate::makepad_widgets::live_design(cx);
|
||||
crate::editor::live_design(cx);
|
||||
crate::app_web::live_design(cx);
|
||||
}
|
||||
// after_new_from_doc
|
||||
}
|
||||
|
||||
impl MatchEvent for App {
|
||||
fn handle_startup(&mut self, _cx: &mut Cx) {}
|
||||
|
||||
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {}
|
||||
}
|
||||
|
||||
impl AppMain for App {
|
||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
|
||||
self.match_event(cx, event);
|
||||
self.ui.handle_event(cx, event, &mut Scope::empty());
|
||||
}
|
||||
}
|
88
src/app_web.rs
Normal file
88
src/app_web.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
use makepad_widgets::*;
|
||||
|
||||
use crate::editor::fish_doc::FishDoc;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import crate::editor::fish_patch_editor::*;
|
||||
import crate::editor::fish_block_editor::*;
|
||||
import crate::editor::fish_theme::*;
|
||||
import crate::editor::fish_connection_widget::*;
|
||||
import crate::editor::fish_selector_widget::*;
|
||||
|
||||
AppWebUI = {{AppWebUI}} {
|
||||
<View>{
|
||||
flow: Down,
|
||||
patcheditorscreen = <View>{
|
||||
flow: Down
|
||||
<View>
|
||||
{
|
||||
flow: Right
|
||||
height: Fit;
|
||||
undobutton = <Button>{text:"Undo"}
|
||||
redobutton = <Button>{text:"Redo"}
|
||||
addblockbutton = <Button>{text:"New Block"}
|
||||
|
||||
}
|
||||
patchedit = <FishPatchEditor>{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Live, Widget)]
|
||||
pub struct AppWebUI {
|
||||
#[deref]
|
||||
ui: Window,
|
||||
#[rust]
|
||||
counter: usize,
|
||||
#[rust]
|
||||
document: FishDoc,
|
||||
}
|
||||
|
||||
impl LiveHook for AppWebUI {
|
||||
fn after_new_from_doc(&mut self, _cx: &mut Cx) {
|
||||
self.document = FishDoc::create_test_doc();
|
||||
println!("after_new_from_doc(): starting some kind of a loop");
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for AppWebUI {
|
||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
|
||||
self.ui
|
||||
.handle_event(cx, event, &mut Scope::with_data(&mut self.document));
|
||||
self.widget_match_event(cx, event, scope);
|
||||
}
|
||||
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
|
||||
let editor_screen = self.ui.view(id!(patcheditorscreen));
|
||||
|
||||
while let Some(_next) = self.ui.draw(cx, scope).step() {
|
||||
editor_screen.draw_all(cx, scope);
|
||||
}
|
||||
DrawStep::done()
|
||||
}
|
||||
}
|
||||
impl WidgetMatchEvent for AppWebUI {
|
||||
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, _scope: &mut Scope) {
|
||||
if self.ui.button(id!(button1)).clicked(&actions) {
|
||||
self.counter += 1;
|
||||
let label = self.ui.label(id!(label1));
|
||||
label.set_text_and_redraw(cx, &format!("Counter: {}", self.counter));
|
||||
}
|
||||
|
||||
if self.ui.button(id!(undobutton)).clicked(&actions) {
|
||||
let _ = self.document.undo().is_ok();
|
||||
self.ui.widget(id!(patchedit)).redraw(cx);
|
||||
}
|
||||
|
||||
if self.ui.button(id!(redobutton)).clicked(&actions) {
|
||||
let _ = self.document.redo().is_ok();
|
||||
self.ui.widget(id!(patchedit)).redraw(cx);
|
||||
}
|
||||
if self.ui.button(id!(addblockbutton)).clicked(&actions) {
|
||||
let _ = self.document.add_block().is_ok();
|
||||
self.ui.widget(id!(patchedit)).redraw(cx);
|
||||
}
|
||||
}
|
||||
}
|
279
src/editor/block_connector_button.rs
Normal file
279
src/editor/block_connector_button.rs
Normal file
|
@ -0,0 +1,279 @@
|
|||
use crate::makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_draw::shader::std::*;
|
||||
|
||||
BlockConnectorButton = {{BlockConnectorButton}} {
|
||||
|
||||
width: 19,
|
||||
height: 19,
|
||||
margin: 1,
|
||||
|
||||
animator: {
|
||||
hover = {
|
||||
default: off,
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.1}}
|
||||
apply: {
|
||||
draw_bg: {pressed: 0.0, hover: 0.0}
|
||||
draw_icon: {pressed: 0.0, hover: 0.0}
|
||||
draw_text: {pressed: 0.0, hover: 0.0}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
on = {
|
||||
from: {
|
||||
all: Forward {duration: 0.1}
|
||||
pressed: Forward {duration: 0.01}
|
||||
}
|
||||
apply: {
|
||||
draw_bg: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
draw_icon: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
draw_text: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
}
|
||||
}
|
||||
|
||||
pressed = {
|
||||
from: {all: Forward {duration: 0.2}}
|
||||
apply: {
|
||||
draw_bg: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
draw_icon: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
draw_text: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
margin: {left:0.0, right: 0.0, top:0.0, bottom: 0.0}
|
||||
align: {x: 0.5, y: 0.5}
|
||||
padding: {left: 0.0, top: 5.0, right: 0.0, bottom: 5.0}
|
||||
|
||||
label_walk: {
|
||||
width: Fit,
|
||||
height: Fit
|
||||
}
|
||||
|
||||
draw_text: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
text_style: <THEME_FONT_LABEL>{
|
||||
font_size: 11.0
|
||||
}
|
||||
fn get_color(self) -> vec4 {
|
||||
return mix(
|
||||
mix(
|
||||
#9,
|
||||
#c,
|
||||
self.hover
|
||||
),
|
||||
#9,
|
||||
self.pressed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
draw_icon: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
fn get_color(self) -> vec4 {
|
||||
return mix(
|
||||
mix(
|
||||
#9,
|
||||
#c,
|
||||
self.hover
|
||||
),
|
||||
#9,
|
||||
self.pressed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
draw_bg: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
uniform border_radius: 3.0
|
||||
instance bodytop: #53
|
||||
instance bodybottom: #5c
|
||||
fn pixel(self) -> vec4 {
|
||||
|
||||
let body = mix(mix(self.bodytop, self.bodybottom, self.hover), #33, self.pressed);
|
||||
|
||||
return body;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, DefaultNone)]
|
||||
pub enum BlockConnectorButtonAction {
|
||||
None,
|
||||
Clicked,
|
||||
Pressed,
|
||||
Released,
|
||||
Move {
|
||||
id: u64,
|
||||
x: f64,
|
||||
y: f64,
|
||||
},
|
||||
ConnectStart {
|
||||
id: u64,
|
||||
x: f64,
|
||||
y: f64,
|
||||
frominput: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Live, LiveHook, Widget)]
|
||||
pub struct BlockConnectorButton {
|
||||
#[animator]
|
||||
animator: Animator,
|
||||
|
||||
#[redraw]
|
||||
#[live]
|
||||
draw_bg: DrawQuad,
|
||||
#[live]
|
||||
draw_text: DrawText,
|
||||
#[live]
|
||||
draw_icon: DrawIcon,
|
||||
#[live]
|
||||
icon_walk: Walk,
|
||||
#[live]
|
||||
label_walk: Walk,
|
||||
#[live]
|
||||
input: bool,
|
||||
#[walk]
|
||||
walk: Walk,
|
||||
|
||||
#[layout]
|
||||
layout: Layout,
|
||||
|
||||
#[live(true)]
|
||||
grab_key_focus: bool,
|
||||
|
||||
#[live]
|
||||
pub text: ArcStringMut,
|
||||
|
||||
#[live]
|
||||
pub blockid: u64,
|
||||
#[rust]
|
||||
pub dragging: bool,
|
||||
}
|
||||
|
||||
impl Widget for BlockConnectorButton {
|
||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
|
||||
let uid = self.widget_uid();
|
||||
self.animator_handle_event(cx, event);
|
||||
match event.hits(cx, self.draw_bg.area()) {
|
||||
Hit::FingerMove(fe) => {
|
||||
if self.dragging {
|
||||
cx.widget_action(
|
||||
uid,
|
||||
&scope.path,
|
||||
BlockConnectorButtonAction::Move {
|
||||
id: self.blockid,
|
||||
x: fe.abs.x,
|
||||
y: fe.abs.y,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Hit::FingerDown(fe) => {
|
||||
if self.grab_key_focus {
|
||||
cx.set_key_focus(self.draw_bg.area());
|
||||
}
|
||||
self.dragging = true;
|
||||
cx.widget_action(
|
||||
uid,
|
||||
&scope.path,
|
||||
BlockConnectorButtonAction::ConnectStart {
|
||||
id: (self.blockid),
|
||||
x: (fe.abs.x),
|
||||
y: (fe.abs.y),
|
||||
frominput: (self.input),
|
||||
},
|
||||
);
|
||||
cx.widget_action(uid, &scope.path, BlockConnectorButtonAction::Pressed);
|
||||
self.animator_play(cx, id!(hover.pressed));
|
||||
}
|
||||
Hit::FingerHoverIn(_) => {
|
||||
cx.set_cursor(MouseCursor::Hand);
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
}
|
||||
Hit::FingerHoverOut(_) => {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
Hit::FingerUp(fe) => {
|
||||
if self.dragging {
|
||||
self.dragging = false;
|
||||
}
|
||||
if fe.is_over {
|
||||
cx.widget_action(uid, &scope.path, BlockConnectorButtonAction::Clicked);
|
||||
cx.widget_action(uid, &scope.path, BlockConnectorButtonAction::Released);
|
||||
if fe.device.has_hovers() {
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
} else {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
} else {
|
||||
cx.widget_action(uid, &scope.path, BlockConnectorButtonAction::Released);
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
|
||||
self.draw_bg.begin(cx, walk, self.layout);
|
||||
self.draw_text
|
||||
.draw_walk(cx, self.label_walk, Align::default(), self.text.as_ref());
|
||||
self.draw_icon.draw_walk(cx, self.icon_walk);
|
||||
self.draw_bg.end(cx);
|
||||
DrawStep::done()
|
||||
}
|
||||
|
||||
fn text(&self) -> String {
|
||||
self.text.as_ref().to_string()
|
||||
}
|
||||
|
||||
fn set_text(&mut self, v: &str) {
|
||||
self.text.as_mut_empty().push_str(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockConnectorButtonRef {
|
||||
#[allow(dead_code)]
|
||||
pub fn clicked(&self, actions: &Actions) -> bool {
|
||||
if let BlockConnectorButtonAction::Clicked =
|
||||
actions.find_widget_action(self.widget_uid()).cast()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn pressed(&self, actions: &Actions) -> bool {
|
||||
if let BlockConnectorButtonAction::Pressed =
|
||||
actions.find_widget_action(self.widget_uid()).cast()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockConnectorButtonSet {
|
||||
#[allow(dead_code)]
|
||||
pub fn clicked(&self, actions: &Actions) -> bool {
|
||||
self.iter().any(|v| v.clicked(actions))
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn pressed(&self, actions: &Actions) -> bool {
|
||||
self.iter().any(|v| v.pressed(actions))
|
||||
}
|
||||
}
|
248
src/editor/block_delete_button.rs
Normal file
248
src/editor/block_delete_button.rs
Normal file
|
@ -0,0 +1,248 @@
|
|||
use crate::makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_draw::shader::std::*;
|
||||
|
||||
BlockDeleteButton = {{BlockDeleteButton}} {
|
||||
|
||||
|
||||
animator: {
|
||||
hover = {
|
||||
default: off,
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.1}}
|
||||
apply: {
|
||||
draw_bg: {pressed: 0.0, hover: 0.0}
|
||||
draw_icon: {pressed: 0.0, hover: 0.0}
|
||||
draw_text: {pressed: 0.0, hover: 0.0}
|
||||
}
|
||||
}
|
||||
|
||||
on = {
|
||||
from: {
|
||||
all: Forward {duration: 0.1}
|
||||
pressed: Forward {duration: 0.01}
|
||||
}
|
||||
apply: {
|
||||
draw_bg: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
draw_icon: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
draw_text: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
}
|
||||
}
|
||||
|
||||
pressed = {
|
||||
from: {all: Forward {duration: 0.2}}
|
||||
apply: {
|
||||
draw_bg: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
draw_icon: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
draw_text: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
width: Fill,
|
||||
height: Fit,
|
||||
margin: {left:0.0, right: 0.0, top:0.0, bottom: 0.0}
|
||||
align: {x: 0.5, y: 0.5}
|
||||
padding: {left: 0.0, top: 5.0, right: 0.0, bottom: 5.0}
|
||||
|
||||
label_walk: {
|
||||
width: Fit,
|
||||
height: Fit
|
||||
}
|
||||
text: "X",
|
||||
draw_text: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
text_style: <THEME_FONT_LABEL>{
|
||||
font_size: 11.0
|
||||
}
|
||||
|
||||
fn get_color(self) -> vec4 {
|
||||
return mix(
|
||||
mix(
|
||||
#0,
|
||||
#4,
|
||||
self.hover
|
||||
),
|
||||
#9,
|
||||
self.pressed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
draw_icon: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
fn get_color(self) -> vec4 {
|
||||
return mix(
|
||||
mix(
|
||||
#9,
|
||||
#c,
|
||||
self.hover
|
||||
),
|
||||
#9,
|
||||
self.pressed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
draw_bg: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
uniform border_radius: 3.0
|
||||
instance bodytop: #53
|
||||
instance bodybottom: #5c
|
||||
fn pixel(self) -> vec4 {
|
||||
|
||||
//let body = mix(mix(self.bodytop, self.bodybottom, self.hover), #33, self.pressed);
|
||||
|
||||
return vec4(0.,0.,0.,0.);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, DefaultNone)]
|
||||
pub enum BlockDeleteButtonAction {
|
||||
None,
|
||||
Clicked,
|
||||
Pressed,
|
||||
Released,
|
||||
KillBlock { id: u64 },
|
||||
}
|
||||
|
||||
#[derive(Live, LiveHook, Widget)]
|
||||
pub struct BlockDeleteButton {
|
||||
#[animator]
|
||||
animator: Animator,
|
||||
|
||||
#[redraw]
|
||||
#[live]
|
||||
draw_bg: DrawQuad,
|
||||
#[live]
|
||||
draw_text: DrawText,
|
||||
#[live]
|
||||
draw_icon: DrawIcon,
|
||||
#[live]
|
||||
icon_walk: Walk,
|
||||
#[live]
|
||||
label_walk: Walk,
|
||||
#[walk]
|
||||
walk: Walk,
|
||||
|
||||
#[layout]
|
||||
layout: Layout,
|
||||
|
||||
#[live(true)]
|
||||
grab_key_focus: bool,
|
||||
|
||||
#[live]
|
||||
pub text: ArcStringMut,
|
||||
|
||||
#[live]
|
||||
pub blockid: u64,
|
||||
#[rust]
|
||||
pub dragging: bool,
|
||||
}
|
||||
|
||||
impl Widget for BlockDeleteButton {
|
||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
|
||||
let uid = self.widget_uid();
|
||||
self.animator_handle_event(cx, event);
|
||||
match event.hits(cx, self.draw_bg.area()) {
|
||||
Hit::FingerMove(_fe) => if self.dragging {},
|
||||
Hit::FingerDown(_fe) => {
|
||||
if self.grab_key_focus {
|
||||
cx.set_key_focus(self.draw_bg.area());
|
||||
}
|
||||
self.dragging = true;
|
||||
|
||||
cx.widget_action(uid, &scope.path, BlockDeleteButtonAction::Pressed);
|
||||
self.animator_play(cx, id!(hover.pressed));
|
||||
}
|
||||
Hit::FingerHoverIn(_) => {
|
||||
cx.set_cursor(MouseCursor::Hand);
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
}
|
||||
Hit::FingerHoverOut(_) => {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
Hit::FingerUp(fe) => {
|
||||
if self.dragging {
|
||||
self.dragging = false;
|
||||
}
|
||||
if fe.is_over {
|
||||
cx.widget_action(uid, &scope.path, BlockDeleteButtonAction::Clicked);
|
||||
cx.widget_action(
|
||||
uid,
|
||||
&scope.path,
|
||||
BlockDeleteButtonAction::KillBlock { id: self.blockid },
|
||||
);
|
||||
if fe.device.has_hovers() {
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
} else {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
} else {
|
||||
cx.widget_action(uid, &scope.path, BlockDeleteButtonAction::Released);
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
|
||||
self.draw_bg.begin(cx, walk, self.layout);
|
||||
self.draw_text
|
||||
.draw_walk(cx, self.label_walk, Align::default(), self.text.as_ref());
|
||||
self.draw_icon.draw_walk(cx, self.icon_walk);
|
||||
self.draw_bg.end(cx);
|
||||
DrawStep::done()
|
||||
}
|
||||
|
||||
fn text(&self) -> String {
|
||||
self.text.as_ref().to_string()
|
||||
}
|
||||
|
||||
fn set_text(&mut self, v: &str) {
|
||||
self.text.as_mut_empty().push_str(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDeleteButtonRef {
|
||||
#[allow(dead_code)]
|
||||
pub fn clicked(&self, actions: &Actions) -> bool {
|
||||
if let BlockDeleteButtonAction::Clicked =
|
||||
actions.find_widget_action(self.widget_uid()).cast()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn pressed(&self, actions: &Actions) -> bool {
|
||||
if let BlockDeleteButtonAction::Pressed =
|
||||
actions.find_widget_action(self.widget_uid()).cast()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDeleteButtonSet {
|
||||
#[allow(dead_code)]
|
||||
pub fn clicked(&self, actions: &Actions) -> bool {
|
||||
self.iter().any(|v| v.clicked(actions))
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn pressed(&self, actions: &Actions) -> bool {
|
||||
self.iter().any(|v| v.pressed(actions))
|
||||
}
|
||||
}
|
274
src/editor/block_header_button.rs
Normal file
274
src/editor/block_header_button.rs
Normal file
|
@ -0,0 +1,274 @@
|
|||
use crate::makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_draw::shader::std::*;
|
||||
|
||||
BlockHeaderButton = {{BlockHeaderButton}} {
|
||||
|
||||
|
||||
animator: {
|
||||
hover = {
|
||||
default: off,
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.1}}
|
||||
apply: {
|
||||
draw_bg: {pressed: 0.0, hover: 0.0}
|
||||
draw_icon: {pressed: 0.0, hover: 0.0}
|
||||
draw_text: {pressed: 0.0, hover: 0.0}
|
||||
}
|
||||
}
|
||||
|
||||
on = {
|
||||
from: {
|
||||
all: Forward {duration: 0.1}
|
||||
pressed: Forward {duration: 0.01}
|
||||
}
|
||||
apply: {
|
||||
draw_bg: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
draw_icon: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
draw_text: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
|
||||
}
|
||||
}
|
||||
|
||||
pressed = {
|
||||
from: {all: Forward {duration: 0.2}}
|
||||
apply: {
|
||||
draw_bg: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
draw_icon: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
draw_text: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
width: Fill,
|
||||
height: Fit,
|
||||
margin: {left:0.0, right: 0.0, top:0.0, bottom: 0.0}
|
||||
align: {x: 0.5, y: 0.5}
|
||||
padding: {left: 0.0, top: 5.0, right: 0.0, bottom: 5.0}
|
||||
|
||||
label_walk: {
|
||||
width: Fit,
|
||||
height: Fit
|
||||
}
|
||||
|
||||
draw_text: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
text_style: <THEME_FONT_LABEL>{
|
||||
font_size: 11.0
|
||||
}
|
||||
fn get_color(self) -> vec4 {
|
||||
return mix(
|
||||
mix(
|
||||
#0,
|
||||
#4,
|
||||
self.hover
|
||||
),
|
||||
#9,
|
||||
self.pressed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
draw_icon: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
fn get_color(self) -> vec4 {
|
||||
return mix(
|
||||
mix(
|
||||
#9,
|
||||
#c,
|
||||
self.hover
|
||||
),
|
||||
#9,
|
||||
self.pressed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
draw_bg: {
|
||||
instance hover: 0.0
|
||||
instance pressed: 0.0
|
||||
uniform border_radius: 3.0
|
||||
instance bodytop: #53
|
||||
instance bodybottom: #5c
|
||||
fn pixel(self) -> vec4 {
|
||||
|
||||
//let body = mix(mix(self.bodytop, self.bodybottom, self.hover), #33, self.pressed);
|
||||
|
||||
return vec4(0.,0.,0.,0.);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Debug, DefaultNone)]
|
||||
pub enum BlockHeaderButtonAction {
|
||||
None,
|
||||
Clicked,
|
||||
Pressed,
|
||||
Released,
|
||||
Select { id: u64 },
|
||||
Move { id: u64, dx: f64, dy: f64 },
|
||||
RecordDragStart { id: u64 },
|
||||
RecordDragEnd { id: u64 },
|
||||
}
|
||||
|
||||
#[derive(Live, LiveHook, Widget)]
|
||||
pub struct BlockHeaderButton {
|
||||
#[animator]
|
||||
animator: Animator,
|
||||
|
||||
#[redraw]
|
||||
#[live]
|
||||
draw_bg: DrawQuad,
|
||||
#[live]
|
||||
draw_text: DrawText,
|
||||
#[live]
|
||||
draw_icon: DrawIcon,
|
||||
#[live]
|
||||
icon_walk: Walk,
|
||||
#[live]
|
||||
label_walk: Walk,
|
||||
#[walk]
|
||||
walk: Walk,
|
||||
|
||||
#[layout]
|
||||
layout: Layout,
|
||||
|
||||
#[live(true)]
|
||||
grab_key_focus: bool,
|
||||
|
||||
#[live]
|
||||
pub text: ArcStringMut,
|
||||
|
||||
#[live]
|
||||
pub blockid: u64,
|
||||
#[rust]
|
||||
pub dragging: bool,
|
||||
|
||||
#[rust]
|
||||
pub draglast: DVec2,
|
||||
}
|
||||
|
||||
impl Widget for BlockHeaderButton {
|
||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
|
||||
let uid = self.widget_uid();
|
||||
self.animator_handle_event(cx, event);
|
||||
match event.hits(cx, self.draw_bg.area()) {
|
||||
Hit::FingerMove(fe) => {
|
||||
if self.dragging {
|
||||
cx.widget_action(
|
||||
uid,
|
||||
&scope.path,
|
||||
BlockHeaderButtonAction::Move {
|
||||
id: self.blockid,
|
||||
dx: fe.abs.x - self.draglast.x,
|
||||
dy: fe.abs.y - self.draglast.y,
|
||||
},
|
||||
);
|
||||
self.draglast = fe.abs;
|
||||
}
|
||||
}
|
||||
Hit::FingerDown(fe) => {
|
||||
if self.grab_key_focus {
|
||||
cx.set_key_focus(self.draw_bg.area());
|
||||
}
|
||||
self.dragging = true;
|
||||
self.draglast = fe.abs;
|
||||
cx.widget_action(uid, &scope.path, BlockHeaderButtonAction::Pressed);
|
||||
|
||||
cx.widget_action(
|
||||
uid,
|
||||
&scope.path,
|
||||
BlockHeaderButtonAction::RecordDragStart { id: self.blockid },
|
||||
);
|
||||
self.animator_play(cx, id!(hover.pressed));
|
||||
}
|
||||
Hit::FingerHoverIn(_) => {
|
||||
cx.set_cursor(MouseCursor::Hand);
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
}
|
||||
Hit::FingerHoverOut(_) => {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
Hit::FingerUp(fe) => {
|
||||
if self.dragging {
|
||||
self.dragging = false;
|
||||
|
||||
cx.widget_action(
|
||||
uid,
|
||||
&scope.path,
|
||||
BlockHeaderButtonAction::RecordDragEnd { id: self.blockid },
|
||||
);
|
||||
}
|
||||
if fe.is_over {
|
||||
cx.widget_action(uid, &scope.path, BlockHeaderButtonAction::Clicked);
|
||||
if fe.device.has_hovers() {
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
} else {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
} else {
|
||||
cx.widget_action(uid, &scope.path, BlockHeaderButtonAction::Released);
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
|
||||
self.draw_bg.begin(cx, walk, self.layout);
|
||||
self.draw_text
|
||||
.draw_walk(cx, self.label_walk, Align::default(), self.text.as_ref());
|
||||
self.draw_icon.draw_walk(cx, self.icon_walk);
|
||||
self.draw_bg.end(cx);
|
||||
DrawStep::done()
|
||||
}
|
||||
|
||||
fn text(&self) -> String {
|
||||
self.text.as_ref().to_string()
|
||||
}
|
||||
|
||||
fn set_text(&mut self, v: &str) {
|
||||
self.text.as_mut_empty().push_str(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHeaderButtonRef {
|
||||
#[allow(dead_code)]
|
||||
pub fn clicked(&self, actions: &Actions) -> bool {
|
||||
if let BlockHeaderButtonAction::Clicked =
|
||||
actions.find_widget_action(self.widget_uid()).cast()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn pressed(&self, actions: &Actions) -> bool {
|
||||
if let BlockHeaderButtonAction::Pressed =
|
||||
actions.find_widget_action(self.widget_uid()).cast()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHeaderButtonSet {
|
||||
#[allow(dead_code)]
|
||||
pub fn clicked(&self, actions: &Actions) -> bool {
|
||||
self.iter().any(|v| v.clicked(actions))
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn pressed(&self, actions: &Actions) -> bool {
|
||||
self.iter().any(|v| v.pressed(actions))
|
||||
}
|
||||
}
|
64
src/editor/fish_block.rs
Normal file
64
src/editor/fish_block.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use crate::editor::fish_block_template::*;
|
||||
use crate::editor::fish_param_storage::*;
|
||||
use crate::editor::fish_ports::*;
|
||||
use crate::makepad_micro_serde::*;
|
||||
use crate::makepad_platform::*;
|
||||
use makepad_widgets::DVec2;
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishBlock {
|
||||
pub id: u64,
|
||||
pub library_id: u64,
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub h: f64,
|
||||
pub w: f64,
|
||||
pub name: String,
|
||||
pub category: FishBlockCategory,
|
||||
pub block_type: String,
|
||||
pub parameters: Vec<FishParamStorage>,
|
||||
pub input_ports: Vec<FishInputPortInstance>,
|
||||
pub output_ports: Vec<FishOutputPortInstance>,
|
||||
}
|
||||
|
||||
impl FishBlock {
|
||||
pub fn _create_test_block(id: u64) -> FishBlock {
|
||||
let mut block = FishBlock::default();
|
||||
block.block_type = String::from(format!("BlockType {:?}", id));
|
||||
block.id = id;
|
||||
|
||||
block
|
||||
}
|
||||
|
||||
pub fn reload_from_string(&mut self, serialized: &str) {
|
||||
*self = FishBlock::deserialize_ron(serialized).expect("deserialize a block");
|
||||
}
|
||||
|
||||
pub fn get_output_instance(&self, id: u64) -> Option<&FishOutputPortInstance> {
|
||||
self.output_ports.iter().find(|&x| x.id == id)
|
||||
}
|
||||
pub fn get_input_instance(&self, id: u64) -> Option<&FishInputPortInstance> {
|
||||
self.input_ports.iter().find(|&x| x.id == id)
|
||||
}
|
||||
|
||||
pub fn is_in_rect(&self, start: DVec2, end: DVec2) -> bool {
|
||||
let block_rect = Rect {
|
||||
pos: DVec2 {
|
||||
x: self.x as f64,
|
||||
y: self.y as f64,
|
||||
},
|
||||
size: DVec2 {
|
||||
x: self.w as f64,
|
||||
y: self.h as f64,
|
||||
},
|
||||
};
|
||||
let containerrect = Rect {
|
||||
pos: start,
|
||||
size: end - start,
|
||||
};
|
||||
if block_rect.intersects(containerrect) || block_rect.inside(containerrect) {
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
}
|
121
src/editor/fish_block_editor.rs
Normal file
121
src/editor/fish_block_editor.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
use crate::makepad_widgets::*;
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_draw::shader::std::*;
|
||||
import crate::editor::fish_theme::*;
|
||||
import crate::editor::block_header_button::*;
|
||||
import crate::editor::block_delete_button::*;
|
||||
|
||||
FishBlockEditor = <View>
|
||||
{
|
||||
margin: 0
|
||||
width: 200
|
||||
height: Fit
|
||||
flow: Down
|
||||
optimize: DrawList
|
||||
|
||||
title = <View>
|
||||
{
|
||||
show_bg: true
|
||||
flow: Down
|
||||
width: Fill
|
||||
height: Fit
|
||||
padding: 0
|
||||
draw_bg:
|
||||
{
|
||||
fn pixel(self) -> vec4
|
||||
{
|
||||
return mix(vec4(1,1,0.6,1), vec4(1,1,0.5,1),self.pos.y);
|
||||
}
|
||||
},
|
||||
topbar = <View>
|
||||
{
|
||||
flow:Right,
|
||||
height: Fit,
|
||||
header = <BlockHeaderButton>
|
||||
{
|
||||
draw_text:
|
||||
{
|
||||
color: #0
|
||||
text_style: <H2_TEXT_BOLD> {}
|
||||
}
|
||||
}
|
||||
delete = <BlockDeleteButton>
|
||||
{
|
||||
width: Fit,
|
||||
draw_text:
|
||||
{
|
||||
color: #0
|
||||
text_style: <H2_TEXT_BOLD> {}
|
||||
}
|
||||
}
|
||||
padding = <View>
|
||||
{
|
||||
width: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
// body = <View>
|
||||
// {
|
||||
// show_bg: true
|
||||
// width: Fill
|
||||
// height: Fit
|
||||
// flow: Down
|
||||
// padding: {left: 30, right: 30, top: 4, bottom: 4}
|
||||
|
||||
// draw_bg: {
|
||||
// fn pixel(self) -> vec4 {
|
||||
// return mix(vec4(1,1,0.9,1), vec4(1,1,0.8,1),self.pos.y);
|
||||
// }
|
||||
// }
|
||||
|
||||
// <FishSlider>{text:"Slider A"}
|
||||
// <FishSlider>{text:"Slider B"}
|
||||
// <FishSlider>{text:"Slider C"}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
FishBlockEditorGenerator = <FishBlockEditor>
|
||||
{
|
||||
title = {draw_bg: { fn pixel(self) -> vec4 { return mix(THEME_COLOR_GENERATOR, THEME_COLOR_GENERATOR_DARK, self.pos.y) }} }
|
||||
// body = {draw_bg: { fn pixel(self) -> vec4 { return THEME_COLOR_GENERATOR_FADE} } }
|
||||
}
|
||||
|
||||
FishBlockEditorEffect = <FishBlockEditor>
|
||||
{
|
||||
title = {draw_bg: { fn pixel(self) -> vec4 { return mix(THEME_COLOR_EFFECT, THEME_COLOR_EFFECT_DARK, self.pos.y) }} }
|
||||
// body = {draw_bg: { fn pixel(self) -> vec4 { return THEME_COLOR_EFFECT_FADE} } }
|
||||
}
|
||||
|
||||
FishBlockEditorMeta = <FishBlockEditor>
|
||||
{
|
||||
title = {draw_bg: { fn pixel(self) -> vec4 { return mix(THEME_COLOR_META, THEME_COLOR_META_DARK, self.pos.y) }} }
|
||||
// body = {draw_bg: { fn pixel(self) -> vec4 { return THEME_COLOR_META_FADE} } }
|
||||
}
|
||||
|
||||
FishBlockEditorUtility = <FishBlockEditor>
|
||||
{
|
||||
title = {draw_bg: { fn pixel(self) -> vec4 { return mix(THEME_COLOR_UTILITY, THEME_COLOR_UTILITY_DARK, self.pos.y) }} }
|
||||
// body = {draw_bg: { fn pixel(self) -> vec4 { return THEME_COLOR_UTILITY_FADE} } }
|
||||
}
|
||||
|
||||
FishBlockEditorModulator = <FishBlockEditor>
|
||||
{
|
||||
title = {draw_bg: { fn pixel(self) -> vec4 { return mix(THEME_COLOR_MODULATION, THEME_COLOR_MODULATION_DARK, self.pos.y) }} }
|
||||
// body = {draw_bg: { fn pixel(self) -> vec4 { return THEME_COLOR_MODULATION_FADE} } }
|
||||
}
|
||||
|
||||
FishBlockEditorEnvelope= <FishBlockEditor>
|
||||
{
|
||||
title = {draw_bg: { fn pixel(self) -> vec4 { return mix(THEME_COLOR_ENVELOPE, THEME_COLOR_ENVELOPE_DARK, self.pos.y) }} }
|
||||
// body = {draw_bg: { fn pixel(self) -> vec4 { return THEME_COLOR_ENVELOPE_FADE} } }
|
||||
}
|
||||
FishBlockEditorFilter= <FishBlockEditor>
|
||||
{
|
||||
title = {draw_bg: { fn pixel(self) -> vec4 { return mix(THEME_COLOR_FILTER, THEME_COLOR_FILTER_DARK, self.pos.y) }} }
|
||||
// body = {draw_bg: { fn pixel(self) -> vec4 { return THEME_COLOR_FILTER_FADE} } }
|
||||
}
|
||||
}
|
233
src/editor/fish_block_template.rs
Normal file
233
src/editor/fish_block_template.rs
Normal file
|
@ -0,0 +1,233 @@
|
|||
use crate::editor::fish_block::*;
|
||||
use crate::editor::fish_param_storage::*;
|
||||
use crate::editor::fish_ports::*;
|
||||
use crate::makepad_micro_serde::*;
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
|
||||
pub enum FishBlockCategory {
|
||||
Meta,
|
||||
Generator,
|
||||
Modulator,
|
||||
Effect,
|
||||
Filter,
|
||||
Envelope,
|
||||
#[default]
|
||||
Utility,
|
||||
}
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
|
||||
pub struct FishBlockTemplate {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub displayname: String,
|
||||
pub description: String,
|
||||
pub creator: String,
|
||||
pub path: String,
|
||||
pub category: FishBlockCategory,
|
||||
|
||||
pub parameters: Vec<FishParamStorage>,
|
||||
pub inputs: Vec<FishInputPort>,
|
||||
pub outputs: Vec<FishOutputPort>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
|
||||
pub struct FishBlockLibrary {
|
||||
pub allblocks: Vec<FishBlockTemplate>,
|
||||
pub nulltemplate: FishBlockTemplate,
|
||||
}
|
||||
|
||||
impl FishBlockLibrary {
|
||||
pub fn populate_library(&mut self, _basepath: &str) {
|
||||
self.nulltemplate = FishBlockTemplate {
|
||||
category: FishBlockCategory::Meta,
|
||||
outputs: vec![],
|
||||
inputs: vec![],
|
||||
parameters: vec![],
|
||||
|
||||
id: 0,
|
||||
name: String::from("Unknown"),
|
||||
displayname: String::from("Unknown"),
|
||||
description: String::from(
|
||||
"This is the empty null block. Is something missing in your library?",
|
||||
),
|
||||
creator: String::from("Stijn Haring-Kuipers"),
|
||||
path: String::from("/null"),
|
||||
};
|
||||
|
||||
self.allblocks.push(FishBlockTemplate {
|
||||
category: FishBlockCategory::Generator,
|
||||
outputs: vec![],
|
||||
inputs: vec![],
|
||||
parameters: vec![],
|
||||
|
||||
id: 0,
|
||||
name: String::from("Oscillator"),
|
||||
displayname: String::from("Oscillator"),
|
||||
description: String::from("Generic osc!"),
|
||||
creator: String::from("Stijn Haring-Kuipers"),
|
||||
path: String::from("/oscillator"),
|
||||
});
|
||||
|
||||
self.allblocks.push(FishBlockTemplate {
|
||||
category: FishBlockCategory::Effect,
|
||||
outputs: vec![],
|
||||
inputs: vec![],
|
||||
parameters: vec![],
|
||||
|
||||
id: 0,
|
||||
name: String::from("Effect"),
|
||||
displayname: String::from("Effect"),
|
||||
description: String::from("Generic effect!"),
|
||||
creator: String::from("Stijn Haring-Kuipers"),
|
||||
path: String::from("/effect"),
|
||||
});
|
||||
|
||||
self.allblocks.push(FishBlockTemplate {
|
||||
category: FishBlockCategory::Filter,
|
||||
outputs: vec![],
|
||||
inputs: vec![],
|
||||
parameters: vec![],
|
||||
|
||||
id: 0,
|
||||
name: String::from("Filter"),
|
||||
displayname: String::from("Filter"),
|
||||
description: String::from("Generic filter!"),
|
||||
creator: String::from("Stijn Haring-Kuipers"),
|
||||
path: String::from("/filter"),
|
||||
});
|
||||
|
||||
self.allblocks.push(FishBlockTemplate {
|
||||
category: FishBlockCategory::Meta,
|
||||
outputs: vec![],
|
||||
inputs: vec![],
|
||||
parameters: vec![],
|
||||
|
||||
id: 0,
|
||||
name: String::from("Meta"),
|
||||
displayname: String::from("Meta"),
|
||||
description: String::from("Generic meta!"),
|
||||
creator: String::from("Stijn Haring-Kuipers"),
|
||||
path: String::from("/meta"),
|
||||
});
|
||||
|
||||
self.allblocks.push(FishBlockTemplate {
|
||||
category: FishBlockCategory::Utility,
|
||||
outputs: vec![],
|
||||
inputs: vec![],
|
||||
parameters: vec![],
|
||||
|
||||
id: 0,
|
||||
name: String::from("Utility"),
|
||||
displayname: String::from("Utility"),
|
||||
description: String::from("Generic utility!"),
|
||||
creator: String::from("Stijn Haring-Kuipers"),
|
||||
path: String::from("/util"),
|
||||
});
|
||||
|
||||
self.allblocks.push(FishBlockTemplate {
|
||||
category: FishBlockCategory::Envelope,
|
||||
outputs: vec![],
|
||||
inputs: vec![],
|
||||
parameters: vec![],
|
||||
|
||||
id: 0,
|
||||
name: String::from("Envelope"),
|
||||
displayname: String::from("Envelope"),
|
||||
description: String::from("Generic envelope!"),
|
||||
creator: String::from("Stijn Haring-Kuipers"),
|
||||
path: String::from("/envelope"),
|
||||
});
|
||||
self.allblocks.push(FishBlockTemplate {
|
||||
category: FishBlockCategory::Modulator,
|
||||
outputs: vec![],
|
||||
inputs: vec![],
|
||||
parameters: vec![],
|
||||
|
||||
id: 0,
|
||||
name: String::from("Modulator"),
|
||||
displayname: String::from("Modulator"),
|
||||
description: String::from("Generic modulator!"),
|
||||
creator: String::from("Stijn Haring-Kuipers"),
|
||||
path: String::from("/modulator"),
|
||||
});
|
||||
|
||||
self.add_dummy_inputs_and_outputs();
|
||||
}
|
||||
|
||||
pub fn add_dummy_inputs_and_outputs(&mut self) {
|
||||
for i in &mut self.allblocks {
|
||||
i.inputs.push(FishInputPort {
|
||||
id: 0,
|
||||
name: String::from("in 1"),
|
||||
datatype: ConnectionType::Audio,
|
||||
});
|
||||
i.outputs.push(FishOutputPort {
|
||||
id: 0,
|
||||
name: String::from("out 1"),
|
||||
datatype: ConnectionType::Audio,
|
||||
});
|
||||
|
||||
i.inputs.push(FishInputPort {
|
||||
id: 1,
|
||||
name: String::from("in 2"),
|
||||
datatype: ConnectionType::Control,
|
||||
});
|
||||
i.outputs.push(FishOutputPort {
|
||||
id: 1,
|
||||
name: String::from("out 2"),
|
||||
datatype: ConnectionType::Control,
|
||||
});
|
||||
|
||||
i.inputs.push(FishInputPort {
|
||||
id: 2,
|
||||
name: String::from("in 3"),
|
||||
datatype: ConnectionType::MIDI,
|
||||
});
|
||||
i.outputs.push(FishOutputPort {
|
||||
id: 2,
|
||||
name: String::from("out 3"),
|
||||
datatype: ConnectionType::MIDI,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_template(&self, name: &str) -> &FishBlockTemplate {
|
||||
if let Some(result) = self.allblocks.iter().find(|v| v.name == name) {
|
||||
return result;
|
||||
}
|
||||
return &self.nulltemplate;
|
||||
}
|
||||
|
||||
pub fn create_instance_from_template(&self, name: &str) -> FishBlock {
|
||||
let t = self.find_template(name);
|
||||
let mut f = FishBlock::default();
|
||||
f.category = t.category.clone();
|
||||
f.library_id = t.id.clone();
|
||||
f.name = t.name.clone();
|
||||
|
||||
for i in &t.inputs {
|
||||
f.input_ports.push(FishInputPortInstance {
|
||||
id: i.id,
|
||||
source_id: i.id,
|
||||
name: i.name.clone(),
|
||||
display_x: 0,
|
||||
display_y: 0,
|
||||
datatype: i.datatype,
|
||||
})
|
||||
}
|
||||
for i in &t.outputs {
|
||||
f.output_ports.push(FishOutputPortInstance {
|
||||
id: i.id,
|
||||
source_id: i.id,
|
||||
name: i.name.clone(),
|
||||
display_x: 0,
|
||||
display_y: 0,
|
||||
datatype: i.datatype,
|
||||
})
|
||||
}
|
||||
|
||||
f
|
||||
}
|
||||
}
|
17
src/editor/fish_connection.rs
Normal file
17
src/editor/fish_connection.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use crate::makepad_micro_serde::*;
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
|
||||
pub struct FishConnection {
|
||||
pub id: u64,
|
||||
pub from_port: u64,
|
||||
pub from_block: u64,
|
||||
pub to_port: u64,
|
||||
pub to_block: u64,
|
||||
}
|
||||
|
||||
impl FishConnection {
|
||||
pub fn reload_from_string(&mut self, serialized: &str) {
|
||||
*self = FishConnection::deserialize_ron(serialized).expect("deserialize a block");
|
||||
}
|
||||
}
|
231
src/editor/fish_connection_widget.rs
Normal file
231
src/editor/fish_connection_widget.rs
Normal file
|
@ -0,0 +1,231 @@
|
|||
#[allow(dead_code)]
|
||||
use crate::{makepad_draw::*, makepad_widgets::widget::*, makepad_widgets::*};
|
||||
|
||||
live_design! {
|
||||
FishConnectionWidget = {{FishConnectionWidget}} {
|
||||
animator: {
|
||||
hover = {
|
||||
default: off
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.15}}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
on = {
|
||||
from: {all: Snap}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
}
|
||||
focus = {
|
||||
default: off
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.0}}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
on = {
|
||||
from: {all: Snap}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
}
|
||||
selected = {
|
||||
default: off
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.0}}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
on = {
|
||||
cursor: Arrow,
|
||||
from: {all: Forward {duration: 0.0}}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, DefaultNone)]
|
||||
pub enum FishConnectionWidgetAction {
|
||||
None,
|
||||
Clicked,
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
#[derive(Live, LiveHook, Widget)]
|
||||
pub struct FishConnectionWidget {
|
||||
#[live]
|
||||
start_pos: DVec2,
|
||||
#[live]
|
||||
end_pos: DVec2,
|
||||
#[animator]
|
||||
animator: Animator,
|
||||
#[redraw]
|
||||
#[live]
|
||||
draw_line: DrawLine,
|
||||
#[walk]
|
||||
walk: Walk,
|
||||
#[layout]
|
||||
layout: Layout,
|
||||
#[live(true)]
|
||||
grab_key_focus: bool,
|
||||
#[live]
|
||||
pub text: ArcStringMut,
|
||||
|
||||
#[live]
|
||||
pub color: Vec4,
|
||||
|
||||
#[live(0)]
|
||||
pub from_h: i32,
|
||||
#[live(0)]
|
||||
pub to_h: i32,
|
||||
|
||||
#[live(10.0)]
|
||||
pub line_width: f64,
|
||||
|
||||
#[live(-1)]
|
||||
pub from_top: i32,
|
||||
#[live(-1)]
|
||||
pub from_bottom: i32,
|
||||
#[live(-1)]
|
||||
pub to_top: i32,
|
||||
#[live(-1)]
|
||||
pub to_bottom: i32,
|
||||
|
||||
#[live(false)]
|
||||
pub selected: bool,
|
||||
}
|
||||
|
||||
impl Widget for FishConnectionWidget {
|
||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
|
||||
//let uid = self.widget_uid();
|
||||
self.animator_handle_event(cx, event);
|
||||
|
||||
return;
|
||||
/*
|
||||
match event.hits(cx, self.draw_line.area()) {
|
||||
Hit::FingerDown(_fe) => {
|
||||
if self.grab_key_focus {
|
||||
cx.set_key_focus(self.draw_line.area());
|
||||
}
|
||||
cx.widget_action(uid, &scope.path, ButtonAction::Pressed);
|
||||
self.animator_play(cx, id!(hover.pressed));
|
||||
}
|
||||
Hit::FingerHoverIn(_) => {
|
||||
cx.set_cursor(MouseCursor::Hand);
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
}
|
||||
Hit::FingerHoverOut(_) => {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
Hit::FingerUp(fe) => {
|
||||
if fe.is_over {
|
||||
cx.widget_action(uid, &scope.path, ButtonAction::Clicked);
|
||||
if fe.device.has_hovers() {
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
} else {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
} else {
|
||||
cx.widget_action(uid, &scope.path, ButtonAction::Released);
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}*/
|
||||
}
|
||||
|
||||
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
|
||||
let _ = self.draw_walk_fishconnection(cx, walk);
|
||||
DrawStep::done()
|
||||
}
|
||||
|
||||
fn text(&self) -> String {
|
||||
self.text.as_ref().to_string()
|
||||
}
|
||||
|
||||
fn set_text(&mut self, v: &str) {
|
||||
self.text.as_mut_empty().push_str(v);
|
||||
}
|
||||
}
|
||||
|
||||
impl FishConnectionWidget {
|
||||
pub fn draw_walk_fishconnection(&mut self, cx: &mut Cx2d, walk: Walk) {
|
||||
self.draw_line.begin(cx, walk, self.layout);
|
||||
self.draw_line.end(cx);
|
||||
|
||||
let overshoot = 40.;
|
||||
|
||||
if self.end_pos.x < self.start_pos.x + overshoot * 2. {
|
||||
let mut midpoint = (self.end_pos + self.start_pos) * 0.5;
|
||||
|
||||
if self.from_bottom > -1 {
|
||||
if self.from_bottom > self.to_top {
|
||||
midpoint.y = (self.from_top + self.to_bottom) as f64 / 2.0;
|
||||
} else {
|
||||
midpoint.y = (self.to_top + self.from_bottom) as f64 / 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
let points = vec![
|
||||
self.start_pos,
|
||||
self.start_pos + dvec2(overshoot, 0.),
|
||||
dvec2(self.start_pos.x + overshoot, midpoint.y),
|
||||
dvec2(self.end_pos.x - overshoot, midpoint.y),
|
||||
self.end_pos + dvec2(-overshoot, 0.),
|
||||
self.end_pos,
|
||||
];
|
||||
|
||||
/* for i in 0..points.len()-1
|
||||
{
|
||||
self.draw_line.draw_line_abs(
|
||||
cx,
|
||||
points[i],
|
||||
points[i+1],
|
||||
self.color,
|
||||
self.line_width,
|
||||
);
|
||||
|
||||
|
||||
}*/
|
||||
if self.selected {
|
||||
self.draw_line.draw_bezier_abs(
|
||||
cx,
|
||||
&points,
|
||||
vec4(1., 1., 0.0, 1.),
|
||||
self.line_width * 1.3,
|
||||
);
|
||||
}
|
||||
|
||||
self.draw_line
|
||||
.draw_bezier_abs(cx, &points, self.color, self.line_width);
|
||||
} else {
|
||||
let midpoint = (self.end_pos + self.start_pos) * 0.5;
|
||||
let deltatomid = midpoint - self.start_pos;
|
||||
|
||||
let points = vec![
|
||||
self.start_pos,
|
||||
self.start_pos + dvec2(deltatomid.x, 0.),
|
||||
self.end_pos - dvec2(deltatomid.x, 0.),
|
||||
self.end_pos,
|
||||
];
|
||||
if self.selected {
|
||||
self.draw_line.draw_bezier_abs(
|
||||
cx,
|
||||
&points,
|
||||
vec4(1., 1., 0.0, 1.),
|
||||
self.line_width * 1.3,
|
||||
);
|
||||
}
|
||||
self.draw_line
|
||||
.draw_bezier_abs(cx, &points, self.color, self.line_width);
|
||||
}
|
||||
|
||||
// self.draw_line.draw_abs(cx, cx.turtle().unscrolled_rect());
|
||||
}
|
||||
}
|
72
src/editor/fish_doc.rs
Normal file
72
src/editor/fish_doc.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
use crate::editor::fish_block_template::FishBlockLibrary;
|
||||
use crate::editor::fish_patch::*;
|
||||
use crate::makepad_micro_serde::*;
|
||||
|
||||
use std::fs;
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon)]
|
||||
|
||||
pub struct FishDoc {
|
||||
pub name: String,
|
||||
pub lib: FishBlockLibrary,
|
||||
pub patches: Vec<FishPatch>,
|
||||
}
|
||||
impl Default for FishDoc {
|
||||
fn default() -> Self {
|
||||
let m = FishDoc {
|
||||
name: String::new(),
|
||||
patches: vec![],
|
||||
lib: FishBlockLibrary::default(),
|
||||
};
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
impl FishDoc {
|
||||
pub fn save(&self, filename: &str) -> Result<(), String> {
|
||||
let docdata = self.serialize_ron();
|
||||
let _ = fs::write(filename, docdata)
|
||||
.map_err(|err| format!("failed to write to {:?} - {:?}", filename, err));
|
||||
println!("saved to {:?}", filename);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn undo(&mut self) -> Result<(), String> {
|
||||
self.patches[0].undo(&self.lib);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn redo(&mut self) -> Result<(), String> {
|
||||
self.patches[0].redo(&self.lib);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_block(&mut self) -> Result<(), String> {
|
||||
self.patches[0].add_block(&self.lib);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load(&mut self, filename: &str) -> Result<(), String> {
|
||||
let docdata = fs::read_to_string(filename)
|
||||
.map_err(|_| format!("Failed to load {:?}", filename))
|
||||
.unwrap();
|
||||
*self = FishDoc::deserialize_ron(&docdata)
|
||||
.map_err(|_| format!("failed to deserialize {:?}", filename))
|
||||
.unwrap();
|
||||
println!("loaded from {:?}", filename);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_test_doc() -> FishDoc {
|
||||
let mut doc = FishDoc::default();
|
||||
doc.lib.populate_library(&String::from("./"));
|
||||
|
||||
doc.patches.push(FishPatch::create_test_patch(1, &doc.lib));
|
||||
doc.patches.push(FishPatch::create_test_patch(2, &doc.lib));
|
||||
doc.patches.push(FishPatch::create_test_patch(3, &doc.lib));
|
||||
doc.patches.push(FishPatch::create_test_patch(4, &doc.lib));
|
||||
|
||||
doc
|
||||
}
|
||||
}
|
41
src/editor/fish_param_storage.rs
Normal file
41
src/editor/fish_param_storage.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use crate::makepad_micro_serde::*;
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishParamStorage {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub int_value: i32,
|
||||
pub float_value: f32,
|
||||
pub text_value: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, SerRon, DeRon, Default)]
|
||||
pub enum FishParameterType {
|
||||
Float,
|
||||
Int,
|
||||
Bool,
|
||||
String,
|
||||
VolumeDB,
|
||||
RatioDB,
|
||||
Frequency,
|
||||
Ratio,
|
||||
CircularDegrees,
|
||||
ColourHue,
|
||||
ColourSaturation,
|
||||
ColourValue,
|
||||
ColourR,
|
||||
ColourG,
|
||||
ColourB,
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishParamDescriptor {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub parameter_type: FishParameterType,
|
||||
pub default_value: FishParamStorage,
|
||||
pub min_value: FishParamStorage,
|
||||
pub max_value: FishParamStorage,
|
||||
}
|
520
src/editor/fish_patch.rs
Normal file
520
src/editor/fish_patch.rs
Normal file
|
@ -0,0 +1,520 @@
|
|||
use crate::editor::fish_block::*;
|
||||
use crate::editor::fish_block_template::*;
|
||||
use crate::editor::fish_connection::*;
|
||||
use crate::editor::fish_preset::*;
|
||||
|
||||
use crate::makepad_micro_serde::*;
|
||||
use makepad_widgets::*;
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub enum UndoableThing {
|
||||
#[default]
|
||||
OpenMarker,
|
||||
CloseMarker,
|
||||
Block,
|
||||
Connection,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub enum IdAction {
|
||||
#[default]
|
||||
Nop,
|
||||
Modify {
|
||||
id: u64,
|
||||
},
|
||||
Delete {
|
||||
id: u64,
|
||||
},
|
||||
Create {
|
||||
id: u64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct UndoState {
|
||||
pub undo_things: Vec<(UndoableThing, IdAction, String)>,
|
||||
pub redo_things: Vec<(UndoableThing, IdAction, String)>,
|
||||
pub undo_level: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishPatch {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub presets: Vec<FishPreset>,
|
||||
pub blocks: Vec<FishBlock>,
|
||||
pub connections: Vec<FishConnection>,
|
||||
pub creationid: u64,
|
||||
pub undo: UndoState,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishPatchSelection {
|
||||
pub blocks: Vec<u64>,
|
||||
}
|
||||
|
||||
impl FishPatchSelection {
|
||||
pub fn add(&mut self, id: u64) {
|
||||
if self.blocks.contains(&id) {
|
||||
return;
|
||||
}
|
||||
self.blocks.push(id);
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.blocks.clear();
|
||||
}
|
||||
}
|
||||
pub trait FindBlock {
|
||||
fn find_mut(&mut self, id: u64) -> Option<&mut FishBlock>;
|
||||
fn find(&self, id: u64) -> Option<&FishBlock>;
|
||||
}
|
||||
|
||||
impl FindBlock for Vec<FishBlock> {
|
||||
fn find_mut(&mut self, id: u64) -> Option<&mut FishBlock> {
|
||||
self.iter_mut().find(|x| x.id == id)
|
||||
}
|
||||
fn find(&self, id: u64) -> Option<&FishBlock> {
|
||||
self.iter().find(|x| x.id == id)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FindConnection {
|
||||
fn find_mut(&mut self, id: u64) -> Option<&mut FishConnection>;
|
||||
fn find(&self, id: u64) -> Option<&FishConnection>;
|
||||
}
|
||||
|
||||
impl FindConnection for Vec<FishConnection> {
|
||||
fn find_mut(&mut self, id: u64) -> Option<&mut FishConnection> {
|
||||
self.iter_mut().find(|x| x.id == id)
|
||||
}
|
||||
fn find(&self, id: u64) -> Option<&FishConnection> {
|
||||
self.iter().find(|x| x.id == id)
|
||||
}
|
||||
}
|
||||
|
||||
impl FishPatch {
|
||||
pub fn select_rectangle(&mut self, start: DVec2, end: DVec2) -> FishPatchSelection {
|
||||
let mut blocks = Vec::new();
|
||||
for block in self.blocks.iter() {
|
||||
if block.is_in_rect(start, end) {
|
||||
blocks.push(block.id);
|
||||
}
|
||||
}
|
||||
FishPatchSelection { blocks }
|
||||
}
|
||||
|
||||
pub fn connect(&mut self, blockfrom: u64, outputfrom: u64, blockto: u64, intputto: u64) {
|
||||
// todo: check if connection exists
|
||||
self.undo_checkpoint_start();
|
||||
let id = self.get_new_id();
|
||||
self.connections.push(FishConnection {
|
||||
id: id,
|
||||
from_block: blockfrom,
|
||||
to_block: blockto,
|
||||
from_port: outputfrom,
|
||||
to_port: intputto,
|
||||
});
|
||||
self.undo_checkpoint_end();
|
||||
}
|
||||
pub fn get_new_id(&mut self) -> u64 {
|
||||
self.creationid = self.creationid + 1;
|
||||
self.creationid
|
||||
}
|
||||
pub fn undo_checkpoint_start(&mut self) -> usize {
|
||||
self.undo.redo_things.clear();
|
||||
|
||||
self.undo
|
||||
.undo_things
|
||||
.push((UndoableThing::OpenMarker, IdAction::Nop, String::new()));
|
||||
|
||||
self.undo.undo_level = self.undo.undo_level + 1;
|
||||
self.undo.undo_level
|
||||
}
|
||||
|
||||
pub fn undo_checkpoint_end(&mut self) {
|
||||
self.undo
|
||||
.undo_things
|
||||
.push((UndoableThing::CloseMarker, IdAction::Nop, String::new()));
|
||||
self.undo.undo_level = self.undo.undo_level - 1;
|
||||
}
|
||||
|
||||
pub fn undo_checkpoint_end_if_match(&mut self, undolevel: usize) -> usize {
|
||||
if self.undo.undo_level == undolevel {
|
||||
self.undo
|
||||
.undo_things
|
||||
.push((UndoableThing::CloseMarker, IdAction::Nop, String::new()));
|
||||
self.undo.undo_level = self.undo.undo_level - 1;
|
||||
return self.undo.undo_level;
|
||||
}
|
||||
undolevel
|
||||
}
|
||||
|
||||
pub fn get_undo_pair(
|
||||
&mut self,
|
||||
undo: bool,
|
||||
) -> (
|
||||
&mut Vec<(UndoableThing, IdAction, String)>,
|
||||
&mut Vec<(UndoableThing, IdAction, String)>,
|
||||
) {
|
||||
match undo {
|
||||
true => (&mut self.undo.undo_things, &mut self.undo.redo_things),
|
||||
false => (&mut self.undo.redo_things, &mut self.undo.undo_things),
|
||||
}
|
||||
}
|
||||
|
||||
/// pub fn push_action_on_stack(&mut self, bool undo )
|
||||
pub fn action_stack_pump(&mut self, _lib: &FishBlockLibrary, undo: bool) {
|
||||
if self.get_undo_pair(undo).0.len() == 0 {
|
||||
return;
|
||||
}
|
||||
let mut level = 0;
|
||||
let mut done = false;
|
||||
while !done {
|
||||
let item = self.get_undo_pair(undo).0.pop().unwrap();
|
||||
match item.0 {
|
||||
UndoableThing::OpenMarker => {
|
||||
level = level - 1;
|
||||
if level == 0 {
|
||||
done = true;
|
||||
}
|
||||
|
||||
self.get_undo_pair(undo).1.push((
|
||||
UndoableThing::CloseMarker,
|
||||
IdAction::Nop,
|
||||
String::new(),
|
||||
));
|
||||
}
|
||||
|
||||
UndoableThing::CloseMarker => {
|
||||
level = level + 1;
|
||||
self.get_undo_pair(undo).1.push((
|
||||
UndoableThing::OpenMarker,
|
||||
IdAction::Nop,
|
||||
String::new(),
|
||||
));
|
||||
}
|
||||
|
||||
UndoableThing::Block => match item.1 {
|
||||
IdAction::Create { id } => {
|
||||
// undo of create = delete
|
||||
let b = self.get_block(&id).expect("find block");
|
||||
let b_string = b.serialize_ron();
|
||||
|
||||
let index = self.blocks.iter().position(|x| x.id == id).unwrap();
|
||||
self.blocks.remove(index);
|
||||
|
||||
self.get_undo_pair(undo).1.push((
|
||||
UndoableThing::Block,
|
||||
IdAction::Delete { id: id },
|
||||
b_string,
|
||||
));
|
||||
}
|
||||
IdAction::Modify { id } => {
|
||||
// deserialize old state in to existing block
|
||||
let b = self.get_block(&id).expect("find block");
|
||||
let b_string = b.serialize_ron();
|
||||
self.get_undo_pair(undo).1.push((
|
||||
UndoableThing::Block,
|
||||
IdAction::Modify { id: id },
|
||||
b_string,
|
||||
));
|
||||
let b = self.blocks.find_mut(id).expect("find block");
|
||||
b.reload_from_string(&item.2);
|
||||
}
|
||||
IdAction::Delete { id } => {
|
||||
// undo of delete = create
|
||||
let zombie = FishBlock::deserialize_ron(&item.2).expect("create a block");
|
||||
self.blocks.push(zombie);
|
||||
self.get_undo_pair(undo).1.push((
|
||||
UndoableThing::Block,
|
||||
IdAction::Create { id: id },
|
||||
String::new(),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
||||
UndoableThing::Connection => match item.1 {
|
||||
IdAction::Create { id } => {
|
||||
// undo of create = delete
|
||||
let c = self.get_connection(id).expect("find connection");
|
||||
let c_string = c.serialize_ron();
|
||||
|
||||
let index = self.connections.iter().position(|x| x.id == id).unwrap();
|
||||
self.connections.remove(index);
|
||||
|
||||
self.get_undo_pair(undo).1.push((
|
||||
UndoableThing::Connection,
|
||||
IdAction::Delete { id: id },
|
||||
c_string,
|
||||
));
|
||||
}
|
||||
|
||||
IdAction::Modify { id } => {
|
||||
// deserialize old state in to existing block
|
||||
let c = self.get_connection(id).expect("find connection");
|
||||
let c_string = c.serialize_ron();
|
||||
self.get_undo_pair(undo).1.push((
|
||||
UndoableThing::Connection,
|
||||
IdAction::Modify { id: id },
|
||||
c_string,
|
||||
));
|
||||
let c = self.connections.find_mut(id).expect("find connection");
|
||||
c.reload_from_string(&item.2);
|
||||
}
|
||||
|
||||
IdAction::Delete { id } => {
|
||||
// undo of delete = create
|
||||
let zombie =
|
||||
FishConnection::deserialize_ron(&item.2).expect("create a connection");
|
||||
self.connections.push(zombie);
|
||||
self.get_undo_pair(undo).1.push((
|
||||
UndoableThing::Connection,
|
||||
IdAction::Create { id: id },
|
||||
String::new(),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn undo(&mut self, lib: &FishBlockLibrary) {
|
||||
self.action_stack_pump(lib, true);
|
||||
}
|
||||
|
||||
pub fn redo(&mut self, lib: &FishBlockLibrary) {
|
||||
self.action_stack_pump(lib, false);
|
||||
}
|
||||
|
||||
pub fn add_block(&mut self, lib: &FishBlockLibrary) {
|
||||
self.undo_checkpoint_start();
|
||||
self.create_block(lib, String::from("Utility"), 100., 100.);
|
||||
self.undo_checkpoint_end();
|
||||
}
|
||||
|
||||
pub fn remove_connection(&mut self, id: u64) {
|
||||
self.undo_checkpoint_start();
|
||||
|
||||
let c = self.get_connection(id).expect("find block");
|
||||
let cstring = c.serialize_ron();
|
||||
|
||||
println!("deleting connection {}", id);
|
||||
|
||||
let index = self.connections.iter().position(|x| x.id == id).unwrap();
|
||||
self.connections.remove(index);
|
||||
|
||||
self.undo.undo_things.push((
|
||||
UndoableThing::Connection,
|
||||
IdAction::Delete { id: id },
|
||||
cstring,
|
||||
));
|
||||
|
||||
self.undo_checkpoint_end();
|
||||
}
|
||||
|
||||
pub fn remove_block(&mut self, id: u64) {
|
||||
self.undo_checkpoint_start();
|
||||
|
||||
// remove connections involving block
|
||||
// remove preset data for block
|
||||
let mut deleteconns: Vec<u64> = vec![];
|
||||
for i in 0..self.connections.len() {
|
||||
if self.connections[i].from_block == id || self.connections[i].to_block == id {
|
||||
deleteconns.push(self.connections[i].id.clone());
|
||||
}
|
||||
}
|
||||
for i in 0..deleteconns.len() {
|
||||
self.remove_connection(deleteconns[i]);
|
||||
}
|
||||
let b = self.get_block(&id).expect("find block");
|
||||
let bstring = b.serialize_ron();
|
||||
|
||||
let index = self.blocks.iter().position(|x| x.id == id).unwrap();
|
||||
self.blocks.remove(index);
|
||||
|
||||
self.undo
|
||||
.undo_things
|
||||
.push((UndoableThing::Block, IdAction::Delete { id: id }, bstring));
|
||||
|
||||
self.undo_checkpoint_end();
|
||||
}
|
||||
|
||||
pub fn get_block(&self, id: &u64) -> Option<&FishBlock> {
|
||||
self.blocks.iter().find(|&x| x.id == *id)
|
||||
}
|
||||
|
||||
pub fn get_connection(&self, id: u64) -> Option<&FishConnection> {
|
||||
self.connections.iter().find(|&x| x.id == id)
|
||||
}
|
||||
|
||||
pub fn undo_save_block_before_modify(&mut self, id: &u64) {
|
||||
let bopt = self.get_block(id);
|
||||
if bopt.is_some() == false {
|
||||
return;
|
||||
};
|
||||
let b = bopt.unwrap();
|
||||
let bstring = b.serialize_ron();
|
||||
|
||||
self.undo
|
||||
.undo_things
|
||||
.push((UndoableThing::Block, IdAction::Modify { id: *id }, bstring));
|
||||
}
|
||||
|
||||
pub fn move_block(&mut self, id: &u64, x: f64, y: f64) {
|
||||
self.undo_checkpoint_start();
|
||||
self.undo_save_block_before_modify(id);
|
||||
let g = self.blocks.iter_mut().find(|x| x.id == *id);
|
||||
|
||||
if g.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let b = g.unwrap();
|
||||
//let g = self.get_block(id).unwrap();
|
||||
let mult = 1.;
|
||||
let div = 1. / (mult as f64);
|
||||
b.x = floor(x * div).max(0.) * mult;
|
||||
b.y = floor(y * div).max(0.) * mult;
|
||||
|
||||
self.undo_checkpoint_end();
|
||||
}
|
||||
|
||||
pub fn move_selection(&mut self, selection: &FishPatchSelection, dx: f64, dy: f64) {
|
||||
self.undo_checkpoint_start();
|
||||
|
||||
for id in selection.blocks.iter() {
|
||||
self.undo_save_block_before_modify(id);
|
||||
let g = self.blocks.iter_mut().find(|x| x.id == *id);
|
||||
|
||||
if g.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let b = g.unwrap();
|
||||
//let g = self.get_block(id).unwrap();
|
||||
let mult = 1.;
|
||||
let div = 1. / (mult);
|
||||
b.x += dx; //floor(((b.x as f64 + dx) * div) ).max(0.) * mult;
|
||||
b.y += dy; //floor(((b.y as f64 + dy) * div) ).max(0.) * mult;
|
||||
}
|
||||
|
||||
self.undo_checkpoint_end();
|
||||
}
|
||||
|
||||
pub fn create_block(&mut self, lib: &FishBlockLibrary, name: String, x: f64, y: f64) {
|
||||
self.undo_checkpoint_start();
|
||||
let mut b = lib.create_instance_from_template(&name);
|
||||
b.x = x;
|
||||
b.y = y;
|
||||
b.id = LiveId::unique().0;
|
||||
let id = b.id;
|
||||
let b_string = b.serialize_ron();
|
||||
|
||||
self.blocks.push(b);
|
||||
|
||||
self.undo
|
||||
.undo_things
|
||||
.push((UndoableThing::Block, IdAction::Create { id: id }, b_string));
|
||||
|
||||
self.undo_checkpoint_end();
|
||||
}
|
||||
|
||||
pub fn create_test_patch(id: u64, lib: &FishBlockLibrary) -> FishPatch {
|
||||
let mut patch = FishPatch::default();
|
||||
patch.name = String::from(format!("Test Patch {:?}", id));
|
||||
patch.id = id;
|
||||
patch.undo_checkpoint_start();
|
||||
let mut i = 0.;
|
||||
|
||||
patch.create_block(
|
||||
lib,
|
||||
String::from("Oscillator"),
|
||||
i % 3. * 300.,
|
||||
i / 3. * 300. + 100.,
|
||||
);
|
||||
|
||||
i = i + 1.;
|
||||
patch.create_block(
|
||||
lib,
|
||||
String::from("Filter"),
|
||||
i % 3. * 300.,
|
||||
i / 3. * 300. + 100.,
|
||||
);
|
||||
|
||||
i = i + 1.;
|
||||
patch.create_block(
|
||||
lib,
|
||||
String::from("Effect"),
|
||||
i % 3. * 300.,
|
||||
i / 3. * 300. + 100.,
|
||||
);
|
||||
|
||||
i = i + 1.;
|
||||
patch.create_block(
|
||||
lib,
|
||||
String::from("Meta"),
|
||||
i % 3. * 300.,
|
||||
i / 3. * 300. + 100.,
|
||||
);
|
||||
|
||||
i = i + 1.;
|
||||
patch.create_block(
|
||||
lib,
|
||||
String::from("Envelope"),
|
||||
i % 3. * 300.,
|
||||
i / 3. * 300. + 100.,
|
||||
);
|
||||
|
||||
i = i + 1.;
|
||||
patch.create_block(
|
||||
lib,
|
||||
String::from("Modulator"),
|
||||
i % 3. * 300.,
|
||||
i / 3. * 300. + 100.,
|
||||
);
|
||||
i = i + 1.;
|
||||
|
||||
patch.create_block(lib, String::from("Utility"), 0., i / 3. * 300. + 100.); //i=i+1;
|
||||
|
||||
for i in 0..7 {
|
||||
patch.presets.push(FishPreset::create_test_preset(i));
|
||||
}
|
||||
|
||||
for i in 0..8 {
|
||||
let fromidx = i as usize % patch.blocks.len();
|
||||
let toidx = (i as usize + 1) % patch.blocks.len();
|
||||
let fromblock = &patch.blocks[fromidx];
|
||||
let toblock = &patch.blocks[toidx];
|
||||
let fromport = (i as usize + 3) % fromblock.output_ports.len();
|
||||
let toport = (i as usize + 4) % toblock.input_ports.len();
|
||||
|
||||
patch.connect(
|
||||
fromblock.id,
|
||||
fromblock.output_ports[fromport].id,
|
||||
toblock.id,
|
||||
toblock.input_ports[toport].id,
|
||||
);
|
||||
}
|
||||
|
||||
/* for i in 0..7 {
|
||||
let fromidx = i as usize % patch.blocks.len();
|
||||
let toidx = (i as usize + 5) % patch.blocks.len();
|
||||
let fromblock = &patch.blocks[fromidx];
|
||||
let toblock = &patch.blocks[toidx];
|
||||
let fromport = (i as usize + 4) % fromblock.output_ports.len();
|
||||
let toport = (i as usize + 5) % toblock.input_ports.len();
|
||||
|
||||
patch.connect(
|
||||
fromblock.id,
|
||||
fromblock.output_ports[fromport].id,
|
||||
toblock.id,
|
||||
toblock.input_ports[toport].id,
|
||||
);
|
||||
}*/
|
||||
patch.undo_checkpoint_end();
|
||||
patch
|
||||
}
|
||||
}
|
318
src/editor/fish_patch_editor.rs
Normal file
318
src/editor/fish_patch_editor.rs
Normal file
|
@ -0,0 +1,318 @@
|
|||
use crate::{
|
||||
editor::{
|
||||
block_delete_button::BlockDeleteButtonAction, block_header_button::BlockHeaderButtonAction,
|
||||
fish_block_template::FishBlockCategory, fish_doc::FishDoc, fish_patch::*,
|
||||
},
|
||||
makepad_draw::*,
|
||||
makepad_widgets::*,
|
||||
};
|
||||
|
||||
live_design! {
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import makepad_widgets::base::*;
|
||||
import crate::editor::fish_block_editor::*;
|
||||
import crate::editor::fish_theme::*;
|
||||
import crate::editor::fish_connection_widget::*;
|
||||
import crate::editor::fish_selector_widget::*;
|
||||
import crate::editor::block_connector_button::*;
|
||||
|
||||
FishPatchEditor = {{FishPatchEditor}} {
|
||||
width: Fill,
|
||||
height: Fill,
|
||||
scroll_bars: <ScrollBars> {}
|
||||
BlockTemplateGenerator = <FishBlockEditorGenerator>{};
|
||||
BlockTemplateMeta = <FishBlockEditorMeta>{};
|
||||
BlockTemplateFilter = <FishBlockEditorFilter>{};
|
||||
BlockTemplateEffect = <FishBlockEditorEffect>{};
|
||||
BlockTemplateModulator = <FishBlockEditorModulator>{};
|
||||
BlockTemplateEnvelope = <FishBlockEditorEnvelope>{};
|
||||
BlockTemplateUtility = <FishBlockEditorUtility>{};
|
||||
|
||||
draw_bg: {
|
||||
fn pixel(self) -> vec4 {
|
||||
return vec4(0.03,0.03,0.03,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Live, Widget)]
|
||||
pub struct FishPatchEditor {
|
||||
#[animator]
|
||||
animator: Animator,
|
||||
#[walk]
|
||||
walk: Walk,
|
||||
#[live]
|
||||
draw_ls: DrawLine,
|
||||
|
||||
#[redraw]
|
||||
#[live]
|
||||
scroll_bars: ScrollBars,
|
||||
#[live]
|
||||
draw_bg: DrawColor,
|
||||
#[rust]
|
||||
unscrolled_rect: Rect,
|
||||
|
||||
#[rust]
|
||||
templates: ComponentMap<LiveId, LivePtr>,
|
||||
#[rust]
|
||||
items: ComponentMap<LiveId, (LiveId, WidgetRef)>,
|
||||
#[rust]
|
||||
selectstart: DVec2,
|
||||
#[rust]
|
||||
selectend: DVec2,
|
||||
#[rust]
|
||||
selecting: bool,
|
||||
#[rust]
|
||||
dragstartx: f64,
|
||||
#[rust]
|
||||
dragstarty: f64,
|
||||
#[rust]
|
||||
active_undo_level: usize,
|
||||
|
||||
#[rust]
|
||||
selection: FishPatchSelection,
|
||||
}
|
||||
|
||||
impl Widget for FishPatchEditor {
|
||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
|
||||
let uid = self.widget_uid();
|
||||
self.animator_handle_event(cx, event);
|
||||
self.scroll_bars.handle_event(cx, event);
|
||||
|
||||
for (_item_id, item) in self.items.values_mut() {
|
||||
let _item_uid = item.widget_uid();
|
||||
|
||||
for action in cx.capture_actions(|cx| item.handle_event(cx, event, scope)) {
|
||||
match action.as_widget_action().cast() {
|
||||
BlockHeaderButtonAction::Select { id } => {
|
||||
self.selection.clear();
|
||||
self.selection.add(id);
|
||||
}
|
||||
BlockHeaderButtonAction::Move { id, dx, dy } => {
|
||||
self.scroll_bars.redraw(cx);
|
||||
let patch = &mut scope.data.get_mut::<FishDoc>().unwrap().patches[0];
|
||||
patch.move_selection(&self.selection, dx, dy);
|
||||
}
|
||||
BlockHeaderButtonAction::RecordDragStart { id } => {
|
||||
let patch = &mut scope.data.get_mut::<FishDoc>().unwrap().patches[0];
|
||||
let block = patch.blocks.find(id);
|
||||
|
||||
if block.is_some() {
|
||||
if !self.selection.blocks.contains(&id) {
|
||||
if self.selection.blocks.len() > 0 {
|
||||
self.selection.clear();
|
||||
}
|
||||
self.selection.add(id);
|
||||
}
|
||||
|
||||
let b = block.unwrap();
|
||||
self.dragstartx = b.x;
|
||||
self.dragstarty = b.y;
|
||||
self.scroll_bars.redraw(cx);
|
||||
self.active_undo_level = patch.undo_checkpoint_start();
|
||||
}
|
||||
}
|
||||
BlockHeaderButtonAction::RecordDragEnd { id: _ } => {
|
||||
let patch = &mut scope.data.get_mut::<FishDoc>().unwrap().patches[0];
|
||||
|
||||
patch.undo_checkpoint_end_if_match(self.active_undo_level);
|
||||
self.active_undo_level = 0;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
match action.as_widget_action().cast() {
|
||||
BlockDeleteButtonAction::KillBlock { id } => {
|
||||
let patch = &mut scope.data.get_mut::<FishDoc>().unwrap().patches[0];
|
||||
patch.remove_block(id);
|
||||
self.scroll_bars.redraw(cx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match event.hits(cx, self.draw_bg.area()) {
|
||||
Hit::FingerDown(fe) => {
|
||||
// if fe.digit_id == live_id!(0).into() {
|
||||
self.selecting = true;
|
||||
let patch = &mut scope.data.get_mut::<FishDoc>().unwrap().patches[0];
|
||||
|
||||
// start selecting
|
||||
self.selectstart = fe.abs.clone();
|
||||
self.selectend = fe.abs.clone();
|
||||
self.selection = patch.select_rectangle(self.selectstart, self.selectend);
|
||||
// }
|
||||
self.scroll_bars.redraw(cx);
|
||||
self.animator_play(cx, id!(hover.pressed));
|
||||
}
|
||||
|
||||
Hit::FingerMove(fe) => {
|
||||
if
|
||||
//fe.digit_id == live_id!(0).into() &&
|
||||
self.selecting {
|
||||
self.selectend = fe.abs.clone();
|
||||
let patch = &mut scope.data.get_mut::<FishDoc>().unwrap().patches[0];
|
||||
self.selection = patch.select_rectangle(self.selectstart, self.selectend);
|
||||
self.scroll_bars.redraw(cx);
|
||||
}
|
||||
}
|
||||
Hit::FingerHoverIn(_) => {
|
||||
cx.set_cursor(MouseCursor::Hand);
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
}
|
||||
Hit::FingerHoverOut(_) => {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
Hit::FingerUp(fe) => {
|
||||
if
|
||||
//fe.digit_id == live_id!(0).into() &&
|
||||
self.selecting {
|
||||
self.selecting = false;
|
||||
// stop selecting
|
||||
self.scroll_bars.redraw(cx);
|
||||
}
|
||||
|
||||
if fe.is_over {
|
||||
// cx.widget_action(uid, &scope.path, ButtonAction::Clicked);
|
||||
// cx.widget_action(uid, &scope.path, ButtonAction::Released);
|
||||
if fe.device.has_hovers() {
|
||||
self.animator_play(cx, id!(hover.on));
|
||||
} else {
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
} else {
|
||||
// cx.widget_action(uid, &scope.path, ButtonAction::Released);
|
||||
self.animator_play(cx, id!(hover.off));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
|
||||
let patch = &mut scope.data.get_mut::<FishDoc>().unwrap().patches[0];
|
||||
//let mut _fullrect = cx.walk_turtle_with_area(&mut self.area, walk);
|
||||
|
||||
self.scroll_bars.begin(cx, walk, Layout::flow_overlay());
|
||||
|
||||
let _turtle_rect = cx.turtle().rect();
|
||||
let scroll_pos = self.scroll_bars.get_scroll_pos();
|
||||
self.unscrolled_rect = cx.turtle().unscrolled_rect();
|
||||
self.draw_bg.draw_abs(cx, cx.turtle().unscrolled_rect());
|
||||
|
||||
for i in &mut patch.blocks.iter_mut() {
|
||||
let item_id = LiveId::from_num(1, i.id as u64);
|
||||
let templateid = match i.category {
|
||||
FishBlockCategory::Effect => live_id!(BlockTemplateEffect),
|
||||
FishBlockCategory::Generator => live_id!(BlockTemplateGenerator),
|
||||
FishBlockCategory::Modulator => live_id!(BlockTemplateModulator),
|
||||
FishBlockCategory::Envelope => live_id!(BlockTemplateEnvelope),
|
||||
FishBlockCategory::Filter => live_id!(BlockTemplateFilter),
|
||||
FishBlockCategory::Meta => live_id!(BlockTemplateMeta),
|
||||
FishBlockCategory::Utility => live_id!(BlockTemplateUtility),
|
||||
};
|
||||
|
||||
let item = self.item(cx, item_id, templateid).unwrap().as_view();
|
||||
|
||||
item.apply_over(
|
||||
cx,
|
||||
live! {title= {topbar = {header= {text: (i.name) , blockid: (i.id)}, delete = {blockid: (i.id)}}},
|
||||
abs_pos: (dvec2(i.x as f64, i.y as f64 )-scroll_pos)},
|
||||
);
|
||||
|
||||
item.draw_all(cx, &mut Scope::empty());
|
||||
let itemarea = item.area().rect(cx);
|
||||
i.h = itemarea.size.y;
|
||||
i.w = itemarea.size.x;
|
||||
|
||||
if self.selection.blocks.contains(&i.id) {
|
||||
self.draw_selected_outline(cx, i.x, i.y, i.w, i.h);
|
||||
}
|
||||
}
|
||||
self.scroll_bars.end(cx);
|
||||
|
||||
DrawStep::done()
|
||||
}
|
||||
}
|
||||
|
||||
impl LiveHook for FishPatchEditor {
|
||||
fn after_new_from_doc(&mut self, _cx: &mut Cx) {}
|
||||
|
||||
fn before_apply(
|
||||
&mut self,
|
||||
_cx: &mut Cx,
|
||||
apply: &mut Apply,
|
||||
_index: usize,
|
||||
_nodes: &[LiveNode],
|
||||
) {
|
||||
if let ApplyFrom::UpdateFromDoc { .. } = apply.from {
|
||||
self.templates.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// hook the apply flow to collect our templates and apply to instanced childnodes
|
||||
fn apply_value_instance(
|
||||
&mut self,
|
||||
cx: &mut Cx,
|
||||
apply: &mut Apply,
|
||||
index: usize,
|
||||
nodes: &[LiveNode],
|
||||
) -> usize {
|
||||
let id = nodes[index].id;
|
||||
match apply.from {
|
||||
ApplyFrom::NewFromDoc { file_id } | ApplyFrom::UpdateFromDoc { file_id } => {
|
||||
if nodes[index].origin.has_prop_type(LivePropType::Instance) {
|
||||
let live_ptr = cx
|
||||
.live_registry
|
||||
.borrow()
|
||||
.file_id_index_to_live_ptr(file_id, index);
|
||||
self.templates.insert(id, live_ptr);
|
||||
// lets apply this thing over all our childnodes with that template
|
||||
for (templ_id, node) in self.items.values_mut() {
|
||||
if *templ_id == id {
|
||||
node.apply(cx, apply, index, nodes);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cx.apply_error_no_matching_field(live_error_origin!(), index, nodes);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
nodes.skip_node(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl FishPatchEditor {
|
||||
pub fn item(&mut self, cx: &mut Cx, id: LiveId, template: LiveId) -> Option<WidgetRef> {
|
||||
if let Some(ptr) = self.templates.get(&template) {
|
||||
let (_, entry) = self.items.get_or_insert(cx, id, |cx| {
|
||||
(template, WidgetRef::new_from_ptr(cx, Some(*ptr)))
|
||||
});
|
||||
return Some(entry.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl FishPatchEditor {
|
||||
pub fn draw_selected_outline(&mut self, cx: &mut Cx2d, x: f64, y: f64, w: f64, h: f64) {
|
||||
let tl = DVec2 {
|
||||
x: x as f64 - 1.5,
|
||||
y: y as f64 - 1.5,
|
||||
};
|
||||
let br = DVec2 {
|
||||
x: (x + w) as f64 + 1.5,
|
||||
y: (y + h) as f64 + 1.5,
|
||||
};
|
||||
|
||||
let bl = DVec2 { x: tl.x, y: br.y };
|
||||
let tr = DVec2 { x: br.x, y: tl.y };
|
||||
let color = vec4(1., 1., 0., 1.);
|
||||
self.draw_ls.draw_line_abs(cx, tl, tr, color, 4.);
|
||||
self.draw_ls.draw_line_abs(cx, tr, br, color, 4.);
|
||||
self.draw_ls.draw_line_abs(cx, br, bl, color, 4.);
|
||||
self.draw_ls.draw_line_abs(cx, tl, bl, color, 4.);
|
||||
}
|
||||
}
|
45
src/editor/fish_ports.rs
Normal file
45
src/editor/fish_ports.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use crate::makepad_micro_serde::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub enum ConnectionType {
|
||||
Audio,
|
||||
Control,
|
||||
MIDI,
|
||||
Gate,
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishInputPort {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub datatype: ConnectionType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishOutputPort {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub datatype: ConnectionType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishInputPortInstance {
|
||||
pub id: u64,
|
||||
pub source_id: u64,
|
||||
pub name: String,
|
||||
pub display_x: i32,
|
||||
pub display_y: i32,
|
||||
pub datatype: ConnectionType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishOutputPortInstance {
|
||||
pub id: u64,
|
||||
pub source_id: u64,
|
||||
pub name: String,
|
||||
pub display_x: i32,
|
||||
pub display_y: i32,
|
||||
pub datatype: ConnectionType,
|
||||
}
|
22
src/editor/fish_preset.rs
Normal file
22
src/editor/fish_preset.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use crate::editor::fish_param_storage::*;
|
||||
|
||||
use crate::makepad_micro_serde::*;
|
||||
|
||||
#[derive(Clone, Debug, SerRon, DeRon, Default)]
|
||||
pub struct FishPreset {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub patch: String,
|
||||
pub tags: Vec<String>,
|
||||
pub values: Vec<FishParamStorage>,
|
||||
}
|
||||
|
||||
impl FishPreset {
|
||||
pub fn create_test_preset(id: i32) -> FishPreset {
|
||||
let mut preset = FishPreset::default();
|
||||
preset.name = String::from(format!("Preset {:?}", id));
|
||||
preset.id = id;
|
||||
|
||||
preset
|
||||
}
|
||||
}
|
124
src/editor/fish_selector_widget.rs
Normal file
124
src/editor/fish_selector_widget.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
#[allow(dead_code)]
|
||||
use crate::{makepad_draw::*, makepad_widgets::widget::*, makepad_widgets::*};
|
||||
|
||||
live_design! {
|
||||
FishSelectorWidget = {{FishSelectorWidget}} {
|
||||
animator: {
|
||||
hover = {
|
||||
default: off
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.15}}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
on = {
|
||||
from: {all: Snap}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
}
|
||||
focus = {
|
||||
default: off
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.0}}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
on = {
|
||||
from: {all: Snap}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
}
|
||||
selected = {
|
||||
default: off
|
||||
off = {
|
||||
from: {all: Forward {duration: 0.0}}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
on = {
|
||||
cursor: Arrow,
|
||||
from: {all: Forward {duration: 0.0}}
|
||||
apply: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
draw_bg: {
|
||||
fn pixel(self) -> vec4 {
|
||||
|
||||
return vec4(0.3,0.3,0.3,0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, DefaultNone)]
|
||||
pub enum FishSelectorWidgetAction {
|
||||
None,
|
||||
Clicked,
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
#[derive(Live, LiveHook, Widget)]
|
||||
pub struct FishSelectorWidget {
|
||||
#[live]
|
||||
start_pos: DVec2,
|
||||
#[live]
|
||||
end_pos: DVec2,
|
||||
#[animator]
|
||||
animator: Animator,
|
||||
#[redraw]
|
||||
#[live]
|
||||
draw_line: DrawLine,
|
||||
#[live]
|
||||
draw_bg: DrawQuad,
|
||||
#[walk]
|
||||
walk: Walk,
|
||||
#[layout]
|
||||
layout: Layout,
|
||||
#[live(true)]
|
||||
grab_key_focus: bool,
|
||||
|
||||
#[live]
|
||||
pub color: Vec4,
|
||||
|
||||
#[live(10.0)]
|
||||
pub line_width: f64,
|
||||
|
||||
}
|
||||
|
||||
impl Widget for FishSelectorWidget {
|
||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
|
||||
//let uid = self.widget_uid();
|
||||
self.animator_handle_event(cx, event);
|
||||
return;
|
||||
}
|
||||
|
||||
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
|
||||
let _ = self.draw_walk_fishselector(cx, walk);
|
||||
DrawStep::done()
|
||||
}
|
||||
}
|
||||
|
||||
impl FishSelectorWidget {
|
||||
pub fn draw_walk_fishselector(&mut self, cx: &mut Cx2d, walk: Walk) {
|
||||
self.draw_line.begin(cx, walk, self.layout);
|
||||
self.draw_line.end(cx);
|
||||
|
||||
let delta = self.end_pos - self.start_pos;
|
||||
let rect = Rect {pos: self.start_pos, size: delta};
|
||||
self.draw_bg.draw_abs(cx, rect);
|
||||
self.draw_line.draw_line_abs(cx, self.start_pos, self.start_pos + dvec2(delta.x, 0.), self.color, self.line_width);
|
||||
self.draw_line.draw_line_abs(cx, self.start_pos, self.start_pos + dvec2(0., delta.y), self.color, self.line_width);
|
||||
self.draw_line.draw_line_abs(cx, self.end_pos, self.end_pos - dvec2(delta.x, 0.), self.color, self.line_width);
|
||||
self.draw_line.draw_line_abs(cx, self.end_pos, self.end_pos - dvec2(0., delta.y), self.color, self.line_width);
|
||||
|
||||
// self.draw_line.draw_abs(cx, cx.turtle().unscrolled_rect());
|
||||
}
|
||||
}
|
178
src/editor/fish_theme.rs
Normal file
178
src/editor/fish_theme.rs
Normal file
|
@ -0,0 +1,178 @@
|
|||
use crate::makepad_platform::*;
|
||||
|
||||
live_design! {
|
||||
|
||||
import makepad_widgets::theme_desktop_dark::*;
|
||||
import makepad_widgets::base::*;
|
||||
import makepad_draw::shader::std::*;
|
||||
const FONT_SIZE_H2 = 10;
|
||||
const FONT_SIZE_REGULAR = 8;
|
||||
|
||||
|
||||
|
||||
|
||||
const SSPACING_1 = 10
|
||||
|
||||
|
||||
const COLOR_DOWN_FULL = #000
|
||||
|
||||
const COLOR_DOWN_0 = #x00000000
|
||||
const COLOR_DOWN_1 = #x00000011
|
||||
const COLOR_DOWN_2 = #x00000022
|
||||
const COLOR_DOWN_3 = #x00000044
|
||||
const COLOR_DOWN_4 = #x00000066
|
||||
const COLOR_DOWN_5 = #x000000AA
|
||||
const COLOR_DOWN_6 = #x000000CC
|
||||
|
||||
const COLOR_UP_0 = #xFFFFFF00
|
||||
const COLOR_UP_1 = #xFFFFFF0A
|
||||
const COLOR_UP_2 = #xFFFFFF10
|
||||
const COLOR_UP_3 = #xFFFFFF20
|
||||
const COLOR_UP_4 = #xFFFFFF40
|
||||
const COLOR_UP_5 = #xFFFFFF66
|
||||
const COLOR_UP_6 = #xFFFFFF88
|
||||
|
||||
const COLOR_UP_7 = #xFFFFFFaa
|
||||
const COLOR_UP_8 = #xFFFFFFaa
|
||||
const COLOR_UP_9 = #xFFFFFFCC
|
||||
const COLOR_UP_FULL = #xFFFFFFFF
|
||||
|
||||
|
||||
const THEME_COLOR_GENERATOR = #F6EB14ff
|
||||
const THEME_COLOR_EFFECT = #4992CEff
|
||||
const THEME_COLOR_MODULATION = #F15751ff
|
||||
const THEME_COLOR_FILTER = #3A3A97ff
|
||||
const THEME_COLOR_ENVELOPE = #EDAD3Aff
|
||||
const THEME_COLOR_META = #D9FF7Fff
|
||||
const THEME_COLOR_UTILITY = #c0c0c0ff
|
||||
|
||||
const CABLE_AUDIO_COLOR = #ffd000ff
|
||||
const CABLE_CONTROL_COLOR = #d0d0d0ff
|
||||
const CABLE_GATE_COLOR = #000040ff
|
||||
const CABLE_MIDI_COLOR = #d0ffd0ff
|
||||
// const CABLE_AUDIO_COLOR = #ffd000ff
|
||||
|
||||
/*
|
||||
const THEME_COLOR_GENERATOR = #ff0000ff
|
||||
const THEME_COLOR_EFFECT = (hsvmod(THEME_COLOR_GENERATOR,60.,0.,0.))
|
||||
const THEME_COLOR_MODULATION = (hsvmod(THEME_COLOR_EFFECT,60.,0.,0.))
|
||||
const THEME_COLOR_FILTER = (hsvmod(THEME_COLOR_MODULATION,60.,0.,0.))
|
||||
const THEME_COLOR_ENVELOPE = (hsvmod(THEME_COLOR_FILTER,60.,0.,0.))
|
||||
const THEME_COLOR_META = (hsvmod(THEME_COLOR_ENVELOPE,60.,0.,0.))
|
||||
const THEME_COLOR_UTILITY = #909090ff
|
||||
*/
|
||||
const THEME_COLOR_GENERATOR_DARK = (hsvmod(THEME_COLOR_GENERATOR, 0.,0.,-0.2))
|
||||
const THEME_COLOR_EFFECT_DARK = (hsvmod(THEME_COLOR_EFFECT, 0.,0.,-0.4))
|
||||
const THEME_COLOR_MODULATION_DARK =(hsvmod(THEME_COLOR_MODULATION, 0.,0.,-0.2))
|
||||
const THEME_COLOR_FILTER_DARK = (hsvmod(THEME_COLOR_FILTER, 0.,0.,-0.2))
|
||||
const THEME_COLOR_ENVELOPE_DARK = (hsvmod(THEME_COLOR_ENVELOPE, 0.,0.,-0.2))
|
||||
const THEME_COLOR_META_DARK = (hsvmod(THEME_COLOR_META, 0.,0.,-0.2))
|
||||
const THEME_COLOR_UTILITY_DARK = #e0e0e0ff
|
||||
|
||||
|
||||
|
||||
const THEME_COLOR_GENERATOR_FADE = (hsvmod(THEME_COLOR_GENERATOR, 0.,-0.6,0.3))
|
||||
const THEME_COLOR_EFFECT_FADE = (hsvmod(THEME_COLOR_EFFECT, 0.,-0.6,0.3))
|
||||
const THEME_COLOR_MODULATION_FADE =(hsvmod(THEME_COLOR_MODULATION, 0.,-0.6,0.3))
|
||||
const THEME_COLOR_FILTER_FADE = (hsvmod(THEME_COLOR_FILTER, 0.,-0.6,0.3))
|
||||
const THEME_COLOR_ENVELOPE_FADE = (hsvmod(THEME_COLOR_ENVELOPE, 0.,-0.6,0.3))
|
||||
const THEME_COLOR_META_FADE = (hsvmod(THEME_COLOR_META, 0.,-0.6,0.3))
|
||||
const THEME_COLOR_UTILITY_FADE = #f0f0f0ff
|
||||
|
||||
|
||||
|
||||
H2_TEXT_BOLD = {
|
||||
font_size: (FONT_SIZE_H2),
|
||||
font: {path: dep("crate://makepad-widgets/resources/IBMPlexSans-SemiBold.ttf")}
|
||||
}
|
||||
|
||||
H2_TEXT_REGULAR = {
|
||||
font_size: (FONT_SIZE_H2),
|
||||
font: {path: dep("crate://makepad-widgets/resources/IBMPlexSans-Text.ttf")}
|
||||
}
|
||||
|
||||
TEXT_BOLD = {
|
||||
font_size: (FONT_SIZE_REGULAR),
|
||||
font: {path: dep("crate://makepad-widgets/resources/IBMPlexSans-SemiBold.ttf")}
|
||||
}
|
||||
|
||||
TEXT_REGULAR = {
|
||||
font_size: (FONT_SIZE_REGULAR),
|
||||
font: {path: dep("crate://makepad-widgets/resources/IBMPlexSans-Text.ttf")}
|
||||
}
|
||||
|
||||
FishSlider = <Slider> {
|
||||
height: 36
|
||||
text: "CutOff1"
|
||||
draw_text: {text_style: <H2_TEXT_BOLD> {}, color: (#0)}
|
||||
text_input: {
|
||||
cursor_margin_bottom: (SSPACING_1),
|
||||
cursor_margin_top: (SSPACING_1),
|
||||
select_pad_edges: (SSPACING_1),
|
||||
cursor_size: (SSPACING_1),
|
||||
empty_message: "0",
|
||||
numeric_only: true,
|
||||
draw_bg: {
|
||||
color: (COLOR_DOWN_0)
|
||||
},
|
||||
draw_text:{
|
||||
color: (#ffff00ff)
|
||||
}
|
||||
}
|
||||
draw_slider: {
|
||||
instance line_color: #f00
|
||||
instance bipolar: 0.0
|
||||
fn pixel(self) -> vec4 {
|
||||
let nub_size = 3
|
||||
|
||||
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
|
||||
let top = 20.0;
|
||||
|
||||
sdf.box(1.0, top, self.rect_size.x - 2, self.rect_size.y - top - 2, 1);
|
||||
sdf.fill_keep(
|
||||
mix(
|
||||
mix((COLOR_DOWN_4), (COLOR_DOWN_4) * 0.1, pow(self.pos.y, 1.0)),
|
||||
mix((COLOR_DOWN_4) * 1.75, (COLOR_DOWN_4) * 0.1, pow(self.pos.y, 1.0)),
|
||||
self.drag
|
||||
)
|
||||
) // Control backdrop gradient
|
||||
|
||||
sdf.stroke(mix(mix(#x00000060, #x00000070, self.drag), #xFFFFFF10, pow(self.pos.y, 10.0)), 1.0) // Control outline
|
||||
let in_side = 5.0;
|
||||
let in_top = 5.0; // Ridge: vertical position
|
||||
sdf.rect(1.0 + in_side, top + in_top, self.rect_size.x - 2 - 2 * in_side, 3);
|
||||
sdf.fill(mix((COLOR_DOWN_4), #00000088, self.drag)); // Ridge color
|
||||
let in_top = 7.0;
|
||||
sdf.rect(1.0 + in_side, top + in_top, self.rect_size.x - 2 - 2 * in_side, 3);
|
||||
sdf.fill(#FFFFFF18); // Ridge: Rim light catcher
|
||||
|
||||
let nub_x = self.slide_pos * (self.rect_size.x - nub_size - in_side * 2 - 9);
|
||||
sdf.move_to(mix(in_side + 3.5, self.rect_size.x * 0.5, self.bipolar), top + in_top);
|
||||
|
||||
sdf.line_to(nub_x + in_side + nub_size * 0.5, top + in_top);
|
||||
sdf.stroke_keep(mix((COLOR_UP_0), self.line_color, self.drag), 1.5)
|
||||
sdf.stroke(
|
||||
mix(mix(self.line_color * 0.85, self.line_color, self.hover), #xFFFFFF80, self.drag),
|
||||
1
|
||||
)
|
||||
|
||||
let nub_x = self.slide_pos * (self.rect_size.x - nub_size - in_side * 2 - 3) - 3;
|
||||
sdf.box(nub_x + in_side, top + 1.0, 12, 12, 1.)
|
||||
|
||||
sdf.fill_keep(mix(mix(#x7, #x8, self.hover), #3, self.pos.y)); // Nub background gradient
|
||||
sdf.stroke(
|
||||
mix(
|
||||
mix(#xa, #xC, self.hover),
|
||||
#0,
|
||||
pow(self.pos.y, 1.5)
|
||||
),
|
||||
1.
|
||||
); // Nub outline gradient
|
||||
|
||||
|
||||
return sdf.result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
29
src/editor/mod.rs
Normal file
29
src/editor/mod.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
pub mod block_connector_button;
|
||||
pub mod block_delete_button;
|
||||
pub mod block_header_button;
|
||||
pub mod fish_block;
|
||||
pub mod fish_block_editor;
|
||||
pub mod fish_block_template;
|
||||
pub mod fish_connection;
|
||||
pub mod fish_connection_widget;
|
||||
pub mod fish_doc;
|
||||
pub mod fish_param_storage;
|
||||
pub mod fish_patch;
|
||||
pub mod fish_patch_editor;
|
||||
pub mod fish_ports;
|
||||
pub mod fish_preset;
|
||||
pub mod fish_selector_widget;
|
||||
pub mod fish_theme;
|
||||
|
||||
use makepad_widgets::Cx;
|
||||
|
||||
pub fn live_design(cx: &mut Cx) {
|
||||
crate::editor::fish_patch_editor::live_design(cx);
|
||||
crate::editor::block_header_button::live_design(cx);
|
||||
crate::editor::block_delete_button::live_design(cx);
|
||||
crate::editor::block_connector_button::live_design(cx);
|
||||
crate::editor::fish_block_editor::live_design(cx);
|
||||
crate::editor::fish_connection_widget::live_design(cx);
|
||||
crate::editor::fish_selector_widget::live_design(cx);
|
||||
crate::editor::fish_theme::live_design(cx);
|
||||
}
|
8
src/lib.rs
Normal file
8
src/lib.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
pub use makepad_widgets;
|
||||
pub use makepad_widgets::makepad_draw;
|
||||
pub use makepad_widgets::makepad_live_id;
|
||||
pub use makepad_widgets::makepad_micro_serde;
|
||||
pub use makepad_widgets::makepad_platform;
|
||||
pub mod app;
|
||||
pub mod app_web;
|
||||
pub mod editor;
|
3
src/main.rs
Normal file
3
src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
mumu::app::app_main()
|
||||
}
|
Loading…
Reference in a new issue