first commit

This commit is contained in:
aOK 2024-08-22 21:09:42 +03:00
commit 5bf207ee08
27 changed files with 3682 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.fish
/target

672
Cargo.lock generated Normal file
View 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
View 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
View file

BIN
resources/colourfish.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
resources/tinrs_mobile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

82
src/app.rs Normal file
View 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
View 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);
}
}
}

View 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))
}
}

View 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))
}
}

View 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
View 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;
}
}

View 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} } }
}
}

View 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
}
}

View 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");
}
}

View 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
View 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
}
}

View 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
View 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
}
}

View 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
View 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
View 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
}
}

View 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
View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
fn main() {
mumu::app::app_main()
}