diff --git a/Cargo.lock b/Cargo.lock index f768c4be..7061a78c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,24 +33,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "aligned" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" -dependencies = [ - "as-slice", -] - -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - [[package]] name = "allocator-api2" version = "0.2.21" @@ -103,12 +85,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" - [[package]] name = "arc-swap" version = "1.9.0" @@ -118,42 +94,12 @@ dependencies = [ "rustversion", ] -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "arkui-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16eca67f4d9750b80b64eca1baa7441279827cea4ec2a1a6e9f33e533a559cad" -dependencies = [ - "ohos-drawing-sys", - "ohos-sys-opaque-types", -] - [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - [[package]] name = "async-compression" version = "0.4.41" @@ -189,49 +135,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "av-scenechange" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" -dependencies = [ - "aligned", - "anyhow", - "arg_enum_proc_macro", - "arrayvec", - "log", - "num-rational", - "num-traits", - "pastey", - "rayon", - "thiserror 2.0.18", - "v_frame", - "y4m", -] - -[[package]] -name = "av1-grain" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" -dependencies = [ - "arrayvec", -] - [[package]] name = "aws-lc-rs" version = "1.16.2" @@ -266,12 +169,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bit_field" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" - [[package]] name = "bitflags" version = "1.3.2" @@ -287,15 +184,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "bitstream-io" -version = "4.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" -dependencies = [ - "core2", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -314,12 +202,6 @@ dependencies = [ "objc2", ] -[[package]] -name = "built" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" - [[package]] name = "bumpalo" version = "3.20.2" @@ -338,12 +220,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - [[package]] name = "bytes" version = "1.11.1" @@ -502,15 +378,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7439becb5fafc780b6f4de382b1a7a3e70234afe783854a4702ee8adbb838609" -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "convert_case" version = "0.10.0" @@ -546,15 +413,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - [[package]] name = "coreaudio-rs" version = "0.14.0" @@ -841,26 +699,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "equator" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] - -[[package]] -name = "equator-macro" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -886,21 +724,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "exr" -version = "1.74.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide 0.8.9", - "rayon-core", - "smallvec", - "zune-inflate", -] - [[package]] name = "extended" version = "0.1.0" @@ -1179,9 +1002,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.14.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" dependencies = [ "color_quant", "weezl", @@ -1646,41 +1469,20 @@ dependencies = [ [[package]] name = "image" -version = "0.25.10" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", - "byteorder-lite", + "byteorder", "color_quant", - "exr", "gif", - "image-webp", - "moxcms", + "jpeg-decoder", "num-traits", "png", - "ravif", "rayon", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error", ] -[[package]] -name = "imgref" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" - [[package]] name = "indexmap" version = "2.13.0" @@ -1721,17 +1523,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8c3414fbf0f1460c97fd92b67dc7517a028c59583ac638c31a1bcb46beb7f4a" -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "intl-memoizer" version = "0.5.3" @@ -1797,15 +1588,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.18" @@ -1893,6 +1675,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" + [[package]] name = "js-sys" version = "0.3.91" @@ -1903,16 +1691,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "keyboard-types" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" -dependencies = [ - "bitflags 2.11.0", - "serde", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1925,28 +1703,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" -[[package]] -name = "lebe" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" - [[package]] name = "libc" version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" -[[package]] -name = "libfuzzer-sys" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" -dependencies = [ - "arbitrary", - "cc", -] - [[package]] name = "libloading" version = "0.8.9" @@ -2038,15 +1800,6 @@ dependencies = [ "logos-codegen", ] -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - [[package]] name = "lru" version = "0.16.3" @@ -2125,12 +1878,11 @@ dependencies = [ [[package]] name = "macroquad" -version = "0.3.25" -source = "git+https://github.com/Mivik/prpr-macroquad?rev=b2eab29#b2eab2927f31477d0d356e6fd27518d7bd16172f" +version = "0.4.15" +source = "git+https://github.com/2278535805/macroquad#5c46fdd330b0cd8c68c99981b30193b5f7c384ab" dependencies = [ - "bumpalo", "fontdue", - "glam 0.30.10", + "glam 0.27.0", "image", "macroquad_macro", "miniquad", @@ -2139,8 +1891,8 @@ dependencies = [ [[package]] name = "macroquad_macro" -version = "0.1.7" -source = "git+https://github.com/Mivik/prpr-macroquad?rev=b2eab29#b2eab2927f31477d0d356e6fd27518d7bd16172f" +version = "0.1.8" +source = "git+https://github.com/2278535805/macroquad#5c46fdd330b0cd8c68c99981b30193b5f7c384ab" [[package]] name = "malloc_buf" @@ -2170,16 +1922,6 @@ dependencies = [ "rawpointer", ] -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - [[package]] name = "memchr" version = "2.8.0" @@ -2217,33 +1959,16 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniquad" -version = "0.3.15" -source = "git+https://github.com/Mivik/prpr-miniquad?rev=0c525a3#0c525a3e0e38a8ab3d6d41bd2749eb8b6f676e4c" +version = "0.4.10" +source = "git+https://github.com/2278535805/miniquad#8ae589abe04694b169e542e8c6d906b60c111951" dependencies = [ "jni 0.21.1", - "keyboard-types", "libc", - "napi-build-ohos", - "napi-derive-ohos", - "napi-ohos", "ndk-context", "ndk-sys 0.2.2", - "objc", - "ohos-hilog-binding", - "ohos-ime-binding", - "ohos-init-binding", - "ohos-input-sys", - "ohos-native-buffer-sys", - "ohos-native-window-sys", - "ohos-qos-sys", - "ohos-sys-opaque-types", - "ohos-xcomponent-binding", - "ohos-xcomponent-sys", - "ohos_enum_macro 0.0.2", - "raw-window-handle", + "objc-rs", "rustls-platform-verifier", "winapi", - "xcomponent-sys", ] [[package]] @@ -2276,16 +2001,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "moxcms" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" -dependencies = [ - "num-traits", - "pxfm", -] - [[package]] name = "nalgebra" version = "0.34.1" @@ -2341,7 +2056,7 @@ version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cea8c5fcecd200491cb05ec15c205e5b408d6ce51ccd6e80c098022b87c2eed" dependencies = [ - "convert_case 0.10.0", + "convert_case", "proc-macro2", "quote", "semver", @@ -2354,7 +2069,7 @@ version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62e951b5919c4e8572e78d0e101c461ea445c777637cde66b0c273cf9f5d7f3" dependencies = [ - "convert_case 0.10.0", + "convert_case", "ctor", "napi-derive-backend-ohos", "proc-macro2", @@ -2422,33 +2137,12 @@ dependencies = [ "jni-sys 0.3.0", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - [[package]] name = "nohash-hasher" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -2547,10 +2241,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0d354e9a302760d07e025701d40534f17dd1fe4c4db955b4e3bd2907c63bdee" [[package]] -name = "objc" -version = "0.2.7" +name = "objc-rs" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "64a1e7069a2525126bf12a9f1f7916835fafade384fb27cabf698e745e2a1eb8" dependencies = [ "malloc_buf", ] @@ -2808,192 +2502,12 @@ dependencies = [ "cc", ] -[[package]] -name = "ohos-arkui-sys" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a19176486bbe46d33830aca4213b07bb46c745200118e57106639db4962e1c3" -dependencies = [ - "napi-sys-ohos", -] - [[package]] name = "ohos-audio-sys" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "892313a2851bbb13c11ba075768ef0d0a9a6a8ae53e54641c9c56eda726dc87f" -[[package]] -name = "ohos-display-binding" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157fe09232fd0af55ed9207d30b47a5f5ff7f84a0673a3b47f65acb7bbc9b8a9" -dependencies = [ - "ohos-display-sys", - "ohos_enum_macro 0.0.1", -] - -[[package]] -name = "ohos-display-sys" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9e1bba4d94159d0a65f1b4e748c148d4bce7930912c424522693f1837dad77" - -[[package]] -name = "ohos-drawing-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156e52a5f46878d2e5b35379385c63d479342f31a936e98d63b8316e15b9cba" -dependencies = [ - "ohos-sys-opaque-types", -] - -[[package]] -name = "ohos-hilog-binding" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "860d1e3c2c5e3217d819a16c815d2d4dcbc7610285d2612d08745a29c353a503" -dependencies = [ - "libc", - "ohos-hilogs-sys", -] - -[[package]] -name = "ohos-hilogs-sys" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed07615005d0f8d7bcf901f89c8ff4870666a9bdb00382f588af383f40c160b7" - -[[package]] -name = "ohos-ime-binding" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b69f3e94816c243aaba0e55f230f87edc19e33c0f9397bfa56c64acb54f776a" -dependencies = [ - "ohos-input-method-sys", - "ohos_enum_macro 0.1.0", -] - -[[package]] -name = "ohos-init-binding" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c0aed2b773de0f45d2200b1c44147a777f9d8e3ad7aa5bc9594c2c4e0df352" -dependencies = [ - "ohos-init-sys", -] - -[[package]] -name = "ohos-init-sys" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f981d2633b0b00f22ffc0a59c5ddead29c99dd285b049eeee2f576eafe53aa" - -[[package]] -name = "ohos-input-method-sys" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225be105b169f45a2bca86498988c1877568d82542c013b345d605eb915e48ae" - -[[package]] -name = "ohos-input-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a1ebb90c96a5373653e87f448154bb778b96f013476abb6046cfe8b01f3bc41" -dependencies = [ - "ohos-sys-opaque-types", -] - -[[package]] -name = "ohos-native-buffer-sys" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e392ef5010fb3e213ca286e53cf3e6530b9ad82ea4f3208cb28d1bb305121f4" - -[[package]] -name = "ohos-native-window-sys" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9bb634cefb5329235c90aac4c7b7f7ab4dd62b6c0d876bed7fe844d4c3fdbb" -dependencies = [ - "ohos-native-buffer-sys", -] - -[[package]] -name = "ohos-qos-sys" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee81b883b19cdff8df0d72d23e6db50b7d0666a586c9af0fd05413b3ead066b8" - -[[package]] -name = "ohos-sys-opaque-types" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97f72e2121a94c07908afa7ae845775f02a51dbc4c691a96710151a00043203d" - -[[package]] -name = "ohos-xcomponent-binding" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f99c43b17408bb43ae53832a7b929dc6a359685f914624e82fec0a52e9d6ac" -dependencies = [ - "napi-derive-ohos", - "napi-ohos", - "napi-sys-ohos", - "ohos-display-binding", - "ohos-xcomponent-sys", - "ohos_enum_macro 0.0.2", - "raw-window-handle", -] - -[[package]] -name = "ohos-xcomponent-sys" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5613209329ff61bc8695871ef985d57fc59ea4d55263e2c2395d8810d063575e" -dependencies = [ - "ohos-arkui-sys", -] - -[[package]] -name = "ohos_enum_macro" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0ee769776099c824423d1686e21b5fd13b464d374d60f7cc25c48c1c836ede1" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "regex", - "syn 2.0.117", -] - -[[package]] -name = "ohos_enum_macro" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4bacd60b52be93024c446f27645d795ab71eb2243bb4c5d87b6238a35341fbd" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "regex", - "syn 2.0.117", -] - -[[package]] -name = "ohos_enum_macro" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc1544b50988de3d636f971135e66c40479ef68e30ef09b15fb911763aab077" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "regex", - "syn 2.0.117", -] - [[package]] name = "once_cell" version = "1.21.4" @@ -3064,12 +2578,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - [[package]] name = "pathdiff" version = "0.2.3" @@ -3152,7 +2660,6 @@ dependencies = [ "lru", "lyon", "macroquad", - "miniquad", "nalgebra", "napi-derive-ohos", "napi-ohos", @@ -3276,11 +2783,11 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" -version = "0.18.1" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ - "bitflags 2.11.0", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", @@ -3370,25 +2877,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "profiling" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" -dependencies = [ - "quote", - "syn 2.0.117", -] - [[package]] name = "prpr" version = "0.7.1" @@ -3420,7 +2908,6 @@ dependencies = [ "lru", "lyon", "macroquad", - "miniquad", "miniz_oxide 0.9.1", "nalgebra", "ndk-context", @@ -3492,24 +2979,12 @@ dependencies = [ "prpr", ] -[[package]] -name = "pxfm" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" - [[package]] name = "quad-rand" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a651516ddc9168ebd67b24afd085a718be02f8858fe406591b013d101ce2f40" -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quick-xml" version = "0.39.2" @@ -3655,56 +3130,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "rav1e" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" -dependencies = [ - "aligned-vec", - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av-scenechange", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "paste", - "profiling", - "rand 0.9.2", - "rand_chacha 0.9.0", - "simd_helpers", - "thiserror 2.0.18", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rayon", - "rgb", -] - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -4254,15 +3679,6 @@ dependencies = [ "simdutf8", ] -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - [[package]] name = "simdutf8" version = "0.1.5" @@ -5017,17 +4433,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "v_frame" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.1" @@ -5706,17 +5111,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" -[[package]] -name = "xcomponent-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69c4f7adcaa8bd155c496a5c6255eb16b9017adf57f63652b2ad800565861bb" -dependencies = [ - "arkui-sys", - "keyboard-types", - "ohos-sys-opaque-types", -] - [[package]] name = "xi-unicode" version = "0.3.0" @@ -5729,12 +5123,6 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" -[[package]] -name = "y4m" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" - [[package]] name = "yoke" version = "0.8.1" @@ -5904,27 +5292,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[package]] -name = "zune-core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7a1c0af6e5d8d1363f4994b7a091ccf963d8b694f7da5b0b9cceb82da2c0a6" -dependencies = [ - "zune-core", -] diff --git a/Cargo.toml b/Cargo.toml index b09e727b..fa55dc68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ fluent = "0.17.0" fluent-syntax = "0.12.0" futures-util = "0.3.32" hex = "0.4.3" -image = { version = "0.25.10", default-features = false } +image = { version = "0.24", default-features = false } inputbox = "0.1.1" lru = "0.16.3" lyon = "1.0.19" @@ -57,8 +57,8 @@ objc2-foundation = "0.3.2" objc2-ui-kit = "0.3.2" objc2-uniform-type-identifiers = "0.3.2" -macroquad = { git = "https://github.com/Mivik/prpr-macroquad", rev = "b2eab29", default-features = false } -miniquad = { git = "https://github.com/Mivik/prpr-miniquad", rev = "0c525a3" } +macroquad = { git = "https://github.com/2278535805/macroquad" } +# macroquad ={ path = "../macroquad" } phira = { path = "phira", default-features = false } phira-mp-client = { git = "https://github.com/TeamFlos/phira-mp", rev = "6967475" } phira-mp-common = { git = "https://github.com/TeamFlos/phira-mp", rev = "6967475" } diff --git a/phira/Cargo.toml b/phira/Cargo.toml index 75d11c5f..57848873 100644 --- a/phira/Cargo.toml +++ b/phira/Cargo.toml @@ -78,7 +78,6 @@ walkdir = { workspace = true } zip = { workspace = true } zstd = "0.13" -miniquad = { workspace = true } phira-mp-client = { workspace = true } phira-mp-common = { workspace = true } sanitize-filename = "0.6.0" diff --git a/phira/src/charts_view.rs b/phira/src/charts_view.rs index 628f3a24..7fdbdb92 100644 --- a/phira/src/charts_view.rs +++ b/phira/src/charts_view.rs @@ -568,7 +568,7 @@ impl ChartsView { .draw_using(&BOLD_FONT); } } else { - ui.fill_path(&path, (*self.icons.r#abstract, r)); + ui.fill_path(&path, (Texture2D::clone(&self.icons.r#abstract), r)); ui.fill_path(&path, semi_black(0.2)); let ct = r.center(); use crate::page::coll::{tl as page_tl, L10N_LOCAL}; @@ -596,7 +596,7 @@ impl ChartsView { let p = if transit.back { p } else { 1. - p }; let r = Rect::tween(&fr, &ui.screen_rect(), p); let path = r.rounded(0.02 * (1. - p)); - ui.fill_path(&path, (*transit.chart.illu.texture.1, r.feather(0.01 * (1. - p)))); + ui.fill_path(&path, (Texture2D::clone(&transit.chart.illu.texture.1), r.feather(0.01 * (1. - p)))); ui.fill_path(&path, semi_black(0.55)); } } diff --git a/phira/src/client/model/user.rs b/phira/src/client/model/user.rs index 35fd43e1..bd13b608 100644 --- a/phira/src/client/model/user.rs +++ b/phira/src/client/model/user.rs @@ -119,7 +119,7 @@ impl User { } pub fn name_color(&self) -> Color { - Color::from_hex_rgb(if self.badges.iter().any(|it| it == "admin") { + Color::from_hex(if self.badges.iter().any(|it| it == "admin") { 0x673ab7 } else if self.badges.iter().any(|it| it == "sponsor") { 0xff7043 diff --git a/phira/src/lib.rs b/phira/src/lib.rs index 81c5b808..a4324656 100644 --- a/phira/src/lib.rs +++ b/phira/src/lib.rs @@ -199,6 +199,7 @@ async fn the_main() -> Result<()> { set_data(data); sync_data(); save_data()?; + miniquad::window::set_ime_enabled(false); let rx = { let (tx, rx) = mpsc::channel(); @@ -212,10 +213,8 @@ async fn the_main() -> Result<()> { rx }; - unsafe { get_internal_gl() } - .quad_context - .display_mut() - .set_pause_resume_listener(on_pause_resume); + // NOTE: set_pause_resume_listener no longer exists in the new miniquad API. + // Pause/resume is now handled through macroquad's window_minimized/window_restored events. if let Some(me) = &get_data().me { anti_addiction_action("startup", Some(format!("phira-{}", me.id))); diff --git a/phira/src/page.rs b/phira/src/page.rs index 973676b0..67003bf1 100644 --- a/phira/src/page.rs +++ b/phira/src/page.rs @@ -199,7 +199,7 @@ impl Illustration { } pub fn shading(&self, r: Rect, t: f32) -> impl Shading { - (*self.texture.0, r, ScaleType::CropCenter, semi_white(self.alpha(t))).into_shading() + (Texture2D::clone(&self.texture.0), r, ScaleType::CropCenter, semi_white(self.alpha(t))).into_shading() } } diff --git a/phira/src/page/coll.rs b/phira/src/page/coll.rs index d0ab2c74..4e6b53fd 100644 --- a/phira/src/page/coll.rs +++ b/phira/src/page/coll.rs @@ -205,7 +205,7 @@ impl Page for CollectionPage { let r = tr.r; let r = Rect::new(f32::tween(&r.x, &sr.x, xp), f32::tween(&r.y, &sr.y, yp), f32::tween(&r.w, &sr.w, xp), f32::tween(&r.h, &sr.h, yp)); let ir = Rect::tween(&tr.ir, &sr, xp); - ui.fill_rect(r, (*tr.illu, ir)); + ui.fill_rect(r, (Texture2D::clone(&tr.illu), ir)); ui.fill_rect(r, semi_black(0.2 + 0.1 * p)); if p >= 1. && tr.t > 0. { self.ok_to_transit = true; diff --git a/phira/src/page/event.rs b/phira/src/page/event.rs index d7182a57..8888a670 100644 --- a/phira/src/page/event.rs +++ b/phira/src/page/event.rs @@ -206,10 +206,10 @@ impl Page for EventPage { self.btn_down.set(ui, Rect::new(0., d, 0., 0.).feather(s)); self.index = (self.scroll.y_scroller.offset / (ui.top * 2.)).round() as usize; ui.with(Rotation2::new(std::f32::consts::FRAC_PI_2).into(), |ui| { - ui.fill_rect(r, (*self.icons.back, r, ScaleType::CropCenter, semi_white(if self.index == 0 { 0.3 } else { 1. }))); + ui.fill_rect(r, (Texture2D::clone(&self.icons.back), r, ScaleType::CropCenter, semi_white(if self.index == 0 { 0.3 } else { 1. }))); }); ui.with(Rotation2::new(-std::f32::consts::FRAC_PI_2).into(), |ui| { - ui.fill_rect(r, (*self.icons.back, r, ScaleType::CropCenter, semi_white(if self.index + 1 >= events.len() { 0.3 } else { 1. }))); + ui.fill_rect(r, (Texture2D::clone(&self.icons.back), r, ScaleType::CropCenter, semi_white(if self.index + 1 >= events.len() { 0.3 } else { 1. }))); }); if events.is_empty() { ui.text(ttl!("list-empty")).anchor(0.5, 0.5).no_baseline().size(1.4).draw(); diff --git a/phira/src/page/favorites.rs b/phira/src/page/favorites.rs index 8de3078b..26301627 100644 --- a/phira/src/page/favorites.rs +++ b/phira/src/page/favorites.rs @@ -1115,7 +1115,7 @@ impl Page for FavoritesPage { ui.dy(-ui.top + 0.03); let s = 0.08; let r = Rect::new(-s, 0., s, s); - ui.fill_rect(r, (*self.icons.menu, r, ScaleType::Fit, WHITE)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.menu), r, ScaleType::Fit, WHITE)); self.operations_menu_btn.set(ui, r); if self.need_show_operations_menu { self.need_show_operations_menu = false; @@ -1127,7 +1127,7 @@ impl Page for FavoritesPage { } if get_data().collection_by_index(index).is_owned() { ui.dx(-r.w - 0.03); - ui.fill_rect(r, (*self.icons.edit, r, ScaleType::Fit, WHITE)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.edit), r, ScaleType::Fit, WHITE)); self.edit_btn.set(ui, r); if self.need_show_edit_menu { self.need_show_edit_menu = false; @@ -1143,9 +1143,9 @@ impl Page for FavoritesPage { r, ( if get_data().collection_by_index(index).id.is_some() { - *self.icons.cloud_check + Texture2D::clone(&self.icons.cloud_check) } else { - *self.icons.cloud_none + Texture2D::clone(&self.icons.cloud_none) }, r, ScaleType::Fit, @@ -1161,12 +1161,12 @@ impl Page for FavoritesPage { self.cloud_menu.show(ui, t, Rect::new(r.x - d, r.bottom() + 0.02, r.w + d, h)); } ui.dx(-r.w - 0.03); - ui.fill_rect(r, (*self.icons.info, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.info), r, ScaleType::Fit)); self.info_btn.set(ui, r); ui.dx(-r.w - 0.03); if self.fetch_like_task.is_some() { - ui.fill_rect(r, (*self.icons.heart_outline, r, ScaleType::Fit, semi_white(0.4))); + ui.fill_rect(r, (Texture2D::clone(&self.icons.heart_outline), r, ScaleType::Fit, semi_white(0.4))); let ct = r.center(); ui.loading( ct.x, @@ -1181,7 +1181,7 @@ impl Page for FavoritesPage { ); } else { let icon = if self.liked { &self.icons.heart } else { &self.icons.heart_outline }; - ui.fill_rect(r, (**icon, r, ScaleType::Fit, if self.liked { ORANGE } else { WHITE })); + ui.fill_rect(r, (Texture2D::clone(icon), r, ScaleType::Fit, if self.liked { ORANGE } else { WHITE })); self.like_btn.set(ui, r); } }); diff --git a/phira/src/page/home.rs b/phira/src/page/home.rs index 023f5498..63e0b9b9 100644 --- a/phira/src/page/home.rs +++ b/phira/src/page/home.rs @@ -286,7 +286,7 @@ impl HomePage { let p = (t - self.board_last_time) / BOARD_TRANSIT_TIME; if p > 1. { self.board_tex_last = None; - ui.fill_path(&path, (**cur, r)); + ui.fill_path(&path, (Texture2D::clone(cur), r)); } else if let Some(last) = &self.board_tex_last { let (cur, last) = if self.board_dir { (last, cur) } else { (cur, last) }; let p = 1. - (1. - p).powi(3); @@ -294,20 +294,20 @@ impl HomePage { clip_rounded_rect(ui, r, rad, |ui| { let mut nr = r; nr.h = r.h * (1. - p); - ui.fill_rect(nr, (**last, nr)); + ui.fill_rect(nr, (Texture2D::clone(last), nr)); nr.h = r.h * p; nr.y = r.bottom() - nr.h; - ui.fill_rect(nr, (**cur, nr)); + ui.fill_rect(nr, (Texture2D::clone(cur), nr)); }); } else { - ui.fill_path(&path, (**cur, r, ScaleType::CropCenter, semi_white(p))); + ui.fill_path(&path, (Texture2D::clone(cur), r, ScaleType::CropCenter, semi_white(p))); } } ui.fill_path(&path, (semi_black(0.7), (r.x, r.y), Color::default(), (r.x + 0.6, r.y))); ui.text(tl!("play")).pos(r.x + pad, r.y + pad).draw(); let r = Rect::new(r.x + 0.02, r.bottom() - 0.18, 0.17, 0.17); - ui.fill_rect(r, (*self.icons.play, r, ScaleType::Fit, semi_white(0.6))); + ui.fill_rect(r, (Texture2D::clone(&self.icons.play), r, ScaleType::Fit, semi_white(0.6))); }); top + 0.03 }) @@ -335,10 +335,10 @@ impl HomePage { let mat = self.btn_other_3d.now(ui, Rect::new(0., top - 0.4, 0.83, 0.23), t); ui.with_gl(mat, |ui| { let r = Rect::new(0., top, 0.38, 0.23); - text_and_icon(s, ui, r, &mut self.btn_event, tl!("event"), *self.icons.medal); + text_and_icon(s, ui, r, &mut self.btn_event, tl!("event"), Texture2D::clone(&self.icons.medal)); let r = Rect::new(r.right() + 0.02, top, 0.29, 0.23); - text_and_icon(s, ui, r, &mut self.btn_respack, tl!("respack"), *self.icons.respack); + text_and_icon(s, ui, r, &mut self.btn_respack, tl!("respack"), Texture2D::clone(&self.icons.respack)); let lf = r.right() + 0.02; @@ -347,7 +347,7 @@ impl HomePage { self.btn_msg.render_shadow(ui, r, t, |ui, path| { ui.fill_path(&path, semi_black(0.4)); let r = r.feather(-0.01); - ui.fill_rect(r, (*self.icons.msg, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.msg), r, ScaleType::Fit)); if self.has_new { let pad = 0.007; ui.fill_circle(r.right() - pad, r.y + pad, 0.01, RED); @@ -358,7 +358,7 @@ impl HomePage { self.btn_settings.render_shadow(ui, r, t, |ui, path| { ui.fill_path(&path, semi_black(0.4)); let r = r.feather(0.004); - ui.fill_rect(r, (*self.icons.settings, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.settings), r, ScaleType::Fit)); }); }); }); @@ -649,7 +649,7 @@ impl Page for HomePage { let p = self.char_appear_p.now(t); let (ox, oy, ow, oh) = self.character.illu_adjust; let r = Rect::new(r.x + ox, r.y + (1. - p) * 0.05 + oy, r.w + ow, r.h + oh); - ui.fill_rect(ui.screen_rect(), (**illu, r, ScaleType::CropCenter, semi_white(p))); + ui.fill_rect(ui.screen_rect(), (Texture2D::clone(illu), r, ScaleType::CropCenter, semi_white(p))); } self.char_btn.set(ui, r); diff --git a/phira/src/page/library.rs b/phira/src/page/library.rs index 2ea63f0f..16ef360b 100644 --- a/phira/src/page/library.rs +++ b/phira/src/page/library.rs @@ -1406,7 +1406,7 @@ impl Page for LibraryPage { self.multi_operation_btn.render_shadow(ui, r, t, |ui, path| { ui.fill_path(&path, WHITE); let cr = r.feather(-0.01); - ui.fill_rect(cr, (*self.icons.r#mod, cr, ScaleType::Fit, BLACK)); + ui.fill_rect(cr, (Texture2D::clone(&self.icons.r#mod), cr, ScaleType::Fit, BLACK)); }); if self.need_show_multi_operation_menu { self.need_show_multi_operation_menu = false; @@ -1425,7 +1425,7 @@ impl Page for LibraryPage { self.multi_select_btn.render_shadow(ui, sr, t, |ui, path| { ui.fill_path(&path, WHITE); let ir = Rect::new(sr.x + 0.04, sr.center().y, 0., 0.).feather(0.025); - ui.fill_rect(ir, (*self.icons.select, ir, ScaleType::Fit, BLACK)); + ui.fill_rect(ir, (Texture2D::clone(&self.icons.select), ir, ScaleType::Fit, BLACK)); ui.text(text) .pos((ir.right() + sr.right() - 0.01) / 2., sr.center().y) .size(0.5) @@ -1447,7 +1447,7 @@ impl Page for LibraryPage { self.multi_select_cancel_btn.render_shadow(ui, r, t, |ui, path| { ui.fill_path(&path, WHITE); let cr = r.feather(-0.01); - ui.fill_rect(cr, (*self.icons.close, cr, ScaleType::Fit, BLACK)); + ui.fill_rect(cr, (Texture2D::clone(&self.icons.close), cr, ScaleType::Fit, BLACK)); }); r.x -= r.w + 0.02; } @@ -1456,7 +1456,7 @@ impl Page for LibraryPage { self.import_btn.render_shadow(ui, r, t, |ui, path| { ui.fill_path(&path, semi_black(0.4)); let cr = r.feather(-0.01); - ui.fill_rect(cr, (*self.icons.plus, cr, ScaleType::Fit)); + ui.fill_rect(cr, (Texture2D::clone(&self.icons.plus), cr, ScaleType::Fit)); }); r.x -= r.w + 0.02; } @@ -1465,7 +1465,7 @@ impl Page for LibraryPage { self.filter_btn.render_shadow(ui, r, t, |ui, path| { ui.fill_path(&path, semi_black(0.4)); let cr = r.feather(-0.01); - ui.fill_rect(cr, (*self.icons.filter, cr, ScaleType::Fit)); + ui.fill_rect(cr, (Texture2D::clone(&self.icons.filter), cr, ScaleType::Fit)); }); r.x -= r.w + 0.02; } else if !multi_select { @@ -1474,9 +1474,9 @@ impl Page for LibraryPage { ui.fill_path(&path, if active { WHITE } else { semi_black(0.4) }); let cr = r.feather(-0.01); if active { - ui.fill_rect(cr, (*self.icons.star, cr, ScaleType::Fit, Color::from_rgba(255, 193, 7, 255))); + ui.fill_rect(cr, (Texture2D::clone(&self.icons.star), cr, ScaleType::Fit, Color::from_rgba(255, 193, 7, 255))); } else { - ui.fill_rect(cr, (*self.icons.star_outline, cr, ScaleType::Fit)); + ui.fill_rect(cr, (Texture2D::clone(&self.icons.star_outline), cr, ScaleType::Fit)); } }); r.x -= r.w + 0.02; @@ -1485,7 +1485,7 @@ impl Page for LibraryPage { self.order_btn.render_shadow(ui, r, t, |ui, path| { ui.fill_path(&path, semi_black(0.4)); let cr = r.feather(-0.01); - ui.fill_rect(cr, (*self.icons.order, cr, ScaleType::Fit)); + ui.fill_rect(cr, (Texture2D::clone(&self.icons.order), cr, ScaleType::Fit)); }); if self.need_show_order_meta_menu { self.need_show_order_meta_menu = false; @@ -1517,11 +1517,11 @@ impl Page for LibraryPage { let mut r = r.feather(-0.01); r.w = r.h; if !empty { - ui.fill_rect(r, (*self.icons.close, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.close), r, ScaleType::Fit)); self.search_clr_btn.set(ui, r); r.x += r.w; } - ui.fill_rect(r, (*self.icons.search, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.search), r, ScaleType::Fit)); ui.text(&self.search_str) .pos(r.right() + 0.01, r.center().y) .anchor(0., 0.5) diff --git a/phira/src/page/offset.rs b/phira/src/page/offset.rs index dcb46ee3..3a491001 100644 --- a/phira/src/page/offset.rs +++ b/phira/src/page/offset.rs @@ -173,7 +173,7 @@ impl Page for OffsetPage { let w = NOTE_WIDTH_RATIO_BASE as f32 * config.note_scale * 2.; let h = w * self.click.height() / self.click.width(); let r = Rect::new(ct.0 - w / 2., ny, w, h); - ui.fill_rect(r, (*self.click, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.click), r, ScaleType::Fit)); self.cali_last = true; } else { if self.cali_last { diff --git a/phira/src/page/respack.rs b/phira/src/page/respack.rs index 2241fd04..81623bc9 100644 --- a/phira/src/page/respack.rs +++ b/phira/src/page/respack.rs @@ -276,7 +276,7 @@ impl Page for ResPackPage { let y = r.y; r.h = tex.height() / tex.width() * r.w; r.y = y - r.h / 2.; - ui.fill_rect(r, (tex, r, ScaleType::Fit)); + ui.fill_rect(r, (tex.clone(), r, ScaleType::Fit)); r.x += r.w * 1.8; r.w *= mh.width() / tex.width(); r.x -= r.w / 2.; @@ -285,11 +285,11 @@ impl Page for ResPackPage { ui.fill_rect(r, (mh, r, ScaleType::Fit)); }; let sp = (cr.h - 0.4) / 2.; - draw(r, *pack.note_style.click, *pack.note_style_mh.click); + draw(r, Texture2D::clone(&pack.note_style.click), Texture2D::clone(&pack.note_style_mh.click)); r.y += sp; - draw(r, *pack.note_style.drag, *pack.note_style_mh.drag); + draw(r, Texture2D::clone(&pack.note_style.drag), Texture2D::clone(&pack.note_style_mh.drag)); r.y += sp; - draw(r, *pack.note_style.flick, *pack.note_style_mh.flick); + draw(r, Texture2D::clone(&pack.note_style.flick), Texture2D::clone(&pack.note_style_mh.flick)); let mut r = Rect::new(0.1, cr.y + 0.1, width, cr.h - 0.38); let draw = |mut r: Rect, style: &NoteStyle, width: f32| { let conv = |r: Rect, tex: &SafeTexture| Rect::new(r.x * tex.width(), r.y * tex.height(), r.w * tex.width(), r.h * tex.height()); @@ -299,7 +299,7 @@ impl Page for ResPackPage { let r2 = Rect::new(r.x, r.y - h * factor, width, h); let r2 = ui.rect_to_global(r2); draw_texture_ex( - *style.hold, + &style.hold, r2.x, r2.y, semi_white(ui.alpha), @@ -314,7 +314,7 @@ impl Page for ResPackPage { let r2 = Rect::new(r.x, r.bottom() - h * (1. - factor), width, h); let r2 = ui.rect_to_global(r2); draw_texture_ex( - *style.hold, + &style.hold, r2.x, r2.y, semi_white(ui.alpha), @@ -328,9 +328,9 @@ impl Page for ResPackPage { let r2 = ui.rect_to_global(r); draw_texture_ex( if pack.info.hold_repeat { - **style.hold_body.as_ref().unwrap() + style.hold_body.as_ref().unwrap() } else { - *style.hold + &style.hold }, r2.x, r2.y, @@ -362,9 +362,9 @@ impl Page for ResPackPage { let rnd = t.div_euclid(inter); let irnd = rnd as u32; let tex = match irnd % 3 { - 0 => *pack.note_style.click, - 1 => *pack.note_style.drag, - 2 => *pack.note_style.flick, + 0 => Texture2D::clone(&pack.note_style.click), + 1 => Texture2D::clone(&pack.note_style.drag), + 2 => Texture2D::clone(&pack.note_style.flick), _ => unreachable!(), }; let st = r.y + 0.06; @@ -401,14 +401,14 @@ impl Page for ResPackPage { self.delete_btn.render_shadow(ui, tr, t, |ui, path| { ui.fill_path(&path, semi_black(0.2)); let r = tr.feather(-0.02); - ui.fill_rect(r, (*self.icons.delete, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.delete), r, ScaleType::Fit)); }); if item.loaded.is_some() { tr.x -= tr.w + 0.02; self.info_btn.render_shadow(ui, tr, t, |ui, path| { ui.fill_path(&path, semi_black(0.2)); let r = tr.feather(-0.02); - ui.fill_rect(r, (*self.icons.info, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.info), r, ScaleType::Fit)); }); } }); diff --git a/phira/src/page/settings.rs b/phira/src/page/settings.rs index 3a01bf47..fdfd16ad 100644 --- a/phira/src/page/settings.rs +++ b/phira/src/page/settings.rs @@ -16,10 +16,10 @@ use macroquad::prelude::*; use once_cell::sync::Lazy; use prpr::{ core::BOLD_FONT, - ext::{open_url, poll_future, semi_white, LocalTask, RectExt, SafeTexture}, + ext::{LocalTask, RectExt, SafeTexture, open_url, poll_future, semi_white}, scene::{request_input, return_input, show_error, show_message, take_input}, task::Task, - ui::{DRectButton, Scroll, Slider, Ui, PREFER_REDUCED_MOTION}, + ui::{DRectButton, InlineInputBox, PREFER_REDUCED_MOTION, Scroll, Slider, Ui}, }; use prpr_l10n::{LanguageIdentifier, LANG_IDENTS, LANG_NAMES}; use reqwest::Url; @@ -289,7 +289,7 @@ fn render_about(ui: &mut Ui, mut r: Rect, icon: &SafeTexture) -> (f32, f32) { let ct = r.center(); let s = 0.1; let ir = Rect::new(ct.x - s, r.y + 0.05, s * 2., s * 2.); - ui.fill_path(&ir.rounded(0.02), (**icon, ir)); + ui.fill_path(&ir.rounded(0.02), (Texture2D::clone(icon), ir)); let staff = &*STAFF_LIST; let text = tl!( @@ -393,6 +393,7 @@ struct GeneralList { server_status_btn: DRectButton, mp_btn: DRectButton, mp_addr_btn: DRectButton, + mp_addr_input: InlineInputBox, #[cfg(not(target_env = "ohos"))] lowq_btn: DRectButton, prefer_reduced_motion_btn: DRectButton, @@ -428,6 +429,7 @@ impl GeneralList { server_status_btn: DRectButton::new(), mp_btn: DRectButton::new(), mp_addr_btn: DRectButton::new(), + mp_addr_input: InlineInputBox::new(), #[cfg(not(target_env = "ohos"))] lowq_btn: DRectButton::new(), prefer_reduced_motion_btn: DRectButton::new(), @@ -474,6 +476,20 @@ impl GeneralList { pub fn touch(&mut self, touch: &Touch, t: f32) -> Result> { let data = get_data_mut(); + if self.mp_addr_input.is_active() { + let submitted = self.mp_addr_input.touch(touch); + if submitted { + let text = self.mp_addr_input.confirm(); + if text.trim().is_empty() { + data.config.mp_address = String::new(); + return Ok(Some(true)); + } + data.config.mp_address = text; + return Ok(Some(true)); + } + return Ok(Some(false)); + } + let config = &mut data.config; if self.lang_btn.touch(touch, t) { return Ok(Some(false)); @@ -507,7 +523,7 @@ impl GeneralList { return Ok(Some(true)); } if self.mp_addr_btn.touch(touch, t) { - request_input("mp_addr", InputBox::new().default_text(&config.mp_address)); + self.mp_addr_input.activate(&config.mp_address, false, false); return Ok(Some(true)); } #[cfg(not(target_env = "ohos"))] @@ -538,6 +554,9 @@ impl GeneralList { pub fn update(&mut self, t: f32) -> Result { self.lang_btn.update(t); let data = get_data_mut(); + if self.mp_addr_input.is_active() { + self.mp_addr_input.update(); + } if self.lang_btn.changed() { data.language = Some(LANG_IDENTS[self.lang_btn.selected()].to_string()); sync_data(); @@ -591,7 +610,7 @@ impl GeneralList { let rt = render_title(ui, tl!("item-lang"), None); let w = 0.06; let r = Rect::new(rt + 0.01, (ITEM_HEIGHT - w) / 2., w, w); - ui.fill_rect(r, (*self.icon_lang, r)); + ui.fill_rect(r, (Texture2D::clone(&self.icon_lang), r)); self.lang_btn.render(ui, rr, t); } @@ -615,7 +634,11 @@ impl GeneralList { } item! { render_title(ui, tl!("item-mp-addr"), Some(tl!("item-mp-addr-sub"))); - self.mp_addr_btn.render_text(ui, rr, t, &config.mp_address, 0.4, false); + if self.mp_addr_input.is_active() { + self.mp_addr_input.render(ui, rr, t, &tl!("item-mp-addr")); + } else { + self.mp_addr_btn.render_text(ui, rr, t, &config.mp_address, 0.4, false) + }; } item! { render_title(ui, tl!("item-prefer-reduced-motion"), Some(tl!("item-prefer-reduced-motion-sub"))); diff --git a/phira/src/rate.rs b/phira/src/rate.rs index d67f3723..e8bf1c83 100644 --- a/phira/src/rate.rs +++ b/phira/src/rate.rs @@ -61,12 +61,12 @@ impl Rate { let pos = (i as f32 - 2.) * (pad + s); let r = Rect::new(pos, s / 2., 0., 0.).feather(s / 2.); if self.score >= (i + 1) * 2 { - ui.fill_rect(r, (**icon_star, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(icon_star), r, ScaleType::Fit)); } else { - ui.fill_rect(r, (**icon_star, r, ScaleType::Fit, cc)); + ui.fill_rect(r, (Texture2D::clone(icon_star), r, ScaleType::Fit, cc)); if self.score == i * 2 + 1 { let hr = Rect { w: r.w / 2., ..r }; - ui.fill_rect(hr, (**icon_star, r, ScaleType::Fit)); + ui.fill_rect(hr, (Texture2D::clone(icon_star), r, ScaleType::Fit)); } } } diff --git a/phira/src/scene/chapter.rs b/phira/src/scene/chapter.rs index b21b8a4b..70dd5de1 100644 --- a/phira/src/scene/chapter.rs +++ b/phira/src/scene/chapter.rs @@ -43,7 +43,7 @@ impl Difficulty { } pub fn color(&self) -> Color { - Color::from_hex_rgb(match self { + Color::from_hex(match self { Self::Easy => 0x16a34a, Self::Hard => 0xf97316, Self::Extreme => 0xdc2626, @@ -282,10 +282,10 @@ impl Scene for ChapterScene { let t = tm.now() as f32; let r = ui.screen_rect(); - ui.fill_rect(r, (*self.cover, r)); + ui.fill_rect(r, (Texture2D::clone(&self.cover), r)); ui.fill_rect(r, semi_black(0.3)); let r = ui.back_rect(); - ui.fill_rect(r, (*self.icons.back, r)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.back), r)); self.btn_back.set(ui, r); use crate::resource::L10N_LOCAL; @@ -333,7 +333,7 @@ impl Scene for ChapterScene { for chart in &mut self.charts { let r = Rect::new(-Self::WIDTH / 2., y - Self::HEIGHT / 2., Self::WIDTH, Self::HEIGHT); chart.btn.render_shadow(ui, r, t, |ui, path| { - ui.fill_path(&path, (*chart.illu, r)); + ui.fill_path(&path, (Texture2D::clone(&chart.illu), r)); ui.fill_path(&path, semi_black(0.4)); let mut t = ui .text(&chart.info.levels[self.diff as usize].level) diff --git a/phira/src/scene/event.rs b/phira/src/scene/event.rs index c05bc447..2d784bf8 100644 --- a/phira/src/scene/event.rs +++ b/phira/src/scene/event.rs @@ -384,7 +384,7 @@ impl Scene for EventScene { let p = 1. - (self.scroll.y_scroller.offset / 0.4).clamp(0., 1.); let r = ui.back_rect(); - ui.fill_rect(r, (*self.icons.back, r, ScaleType::Fit, semi_white(p))); + ui.fill_rect(r, (Texture2D::clone(&self.icons.back), r, ScaleType::Fit, semi_white(p))); self.btn_back.set(ui, r); ui.fill_rect(ui.screen_rect(), semi_black((self.scroll.y_scroller.offset / 0.3).min(1.) * 0.7)); @@ -461,17 +461,17 @@ impl Scene for EventScene { if Utc::now() > self.event.time_end { draw(tl!("btn-ended"), semi_black(0.4)); } else if Utc::now() < self.event.time_start { - draw(tl!("btn-not-started"), Color::from_hex_rgb(0xe3f2fd)); + draw(tl!("btn-not-started"), Color::from_hex(0xe3f2fd)); } else { self.btn_join - .render_shadow(ui, r, t, |ui, path| ui.fill_path(&path, Color::from_hex_rgb(0xf57c00))); + .render_shadow(ui, r, t, |ui, path| ui.fill_path(&path, Color::from_hex(0xf57c00))); let mut text = ui.text(format!("#{}", status.rank.unwrap())).anchor(0., 0.5).no_baseline().size(0.7); let w = text.measure().w; let mut ir = Rect::new(ct.x, ct.y, 0., 0.).feather(r.h / 2. - 0.02); let w = w + 0.01 + ir.w; ir.x += (ir.w - w) / 2.; text.pos(ir.right() + 0.01, ct.y).draw(); - ui.fill_rect(ir, (*self.icons.ldb, ir, ScaleType::Fit)); + ui.fill_rect(ir, (Texture2D::clone(&self.icons.ldb), ir, ScaleType::Fit)); } } else { draw(tl!("btn-join"), bc); diff --git a/phira/src/scene/main.rs b/phira/src/scene/main.rs index 01bdc65a..e82d9a88 100644 --- a/phira/src/scene/main.rs +++ b/phira/src/scene/main.rs @@ -631,8 +631,8 @@ impl Scene for MainScene { set_camera(&ui.camera()); STRIPE_MATERIAL.set_uniform("time", ((tm.real_time() * 0.025) % (std::f64::consts::PI * 2.)) as f32); - gl_use_material(*STRIPE_MATERIAL); - ui.fill_rect(ui.screen_rect(), (*self.background, ui.screen_rect())); + gl_use_material(&STRIPE_MATERIAL); + ui.fill_rect(ui.screen_rect(), (Texture2D::clone(&self.background), ui.screen_rect())); gl_use_default_material(); let s = &mut self.state; @@ -669,7 +669,7 @@ impl Scene for MainScene { 2 => s.fader.for_sub(|f| f.progress(s.t)), _ => 0., } * r.h; - ui.fill_rect(r, (*self.icon_back, r)); + ui.fill_rect(r, (Texture2D::clone(&self.icon_back), r)); }); } @@ -683,7 +683,7 @@ impl Scene for MainScene { let r = Rect::new(self.mp_btn_pos.x, self.mp_btn_pos.y, 0., 0.).feather(r); self.mp_btn.set(ui, r); let r = r.feather(-0.02); - ui.fill_rect(r, (*self.mp_icon, r)); + ui.fill_rect(r, (Texture2D::clone(&self.mp_icon), r)); MP_PANEL.with(|it| { if let Some(panel) = it.borrow_mut().as_mut() { @@ -719,10 +719,9 @@ impl Scene for MainScene { static STRIPE_MATERIAL: Lazy = Lazy::new(|| { load_material( - shader::VERTEX, - shader::FRAGMENT, + ShaderSource::Glsl { vertex: shader::VERTEX, fragment: shader::FRAGMENT }, MaterialParams { - uniforms: vec![("time".to_owned(), UniformType::Float1)], + uniforms: vec![UniformDesc::new("time", UniformType::Float1)], ..Default::default() }, ) diff --git a/phira/src/scene/profile.rs b/phira/src/scene/profile.rs index 325dbd28..9c5bb9e3 100644 --- a/phira/src/scene/profile.rs +++ b/phira/src/scene/profile.rs @@ -264,7 +264,7 @@ impl Scene for ProfileScene { } if self.btn_name.touch(touch) { if let Some(user) = &self.user { - unsafe { get_internal_gl() }.quad_context.clipboard_set(&user.name); + macroquad::miniquad::window::clipboard_set(&user.name); show_message(tl!("name-copied")).ok(); } return Ok(true); @@ -313,9 +313,9 @@ impl Scene for ProfileScene { let t = tm.now() as f32; let r = ui.screen_rect(); - ui.fill_rect(r, (*self.background, r)); + ui.fill_rect(r, (Texture2D::clone(&self.background), r)); let r = ui.back_rect(); - ui.fill_rect(r, (*self.icon_back, r)); + ui.fill_rect(r, (Texture2D::clone(&self.icon_back), r)); self.btn_back.set(ui, r); let r = Rect::new(-0.85, -ui.top + 0.1, 0.6, 2.); @@ -432,14 +432,14 @@ impl Scene for ProfileScene { } item.illu.notify(); item.btn.render_shadow(ui, r, t, |ui, path| { - ui.fill_path(&path, (*item.illu.texture.0, r)); + ui.fill_path(&path, (Texture2D::clone(&item.illu.texture.0), r)); ui.fill_path(&path, semi_black(0.6)); }); let icon = icon_index(item.record.score as _, item.record.full_combo); let s = r.h - pad * 2.; let ir = Rect::new(r.x + pad, r.y + pad, s, s); - ui.fill_rect(ir, (*self.rank_icons[icon], ir, ScaleType::Fit)); + ui.fill_rect(ir, (Texture2D::clone(&self.rank_icons[icon]), ir, ScaleType::Fit)); let lf = ir.right() + 0.02; diff --git a/phira/src/scene/song.rs b/phira/src/scene/song.rs index c28b48be..f1bb021d 100644 --- a/phira/src/scene/song.rs +++ b/phira/src/scene/song.rs @@ -64,6 +64,7 @@ use std::{ collections::{hash_map, BTreeMap, HashMap, VecDeque}, fs::File, io::{BufWriter, Cursor, Seek, Write}, + ops::Deref, path::Path, sync::{ atomic::{AtomicBool, AtomicI32, Ordering}, @@ -2616,12 +2617,12 @@ impl Scene for SongScene { fn render(&mut self, tm: &mut TimeManager, ui: &mut Ui) -> Result<()> { set_camera(&ui.camera()); let t = tm.now() as f32; - ui.fill_rect(ui.screen_rect(), (*self.illu.texture.1, ui.screen_rect())); + ui.fill_rect(ui.screen_rect(), (Texture2D::clone(&self.illu.texture.1), ui.screen_rect())); ui.fill_rect(ui.screen_rect(), semi_black(0.55)); let r = ui.back_rect(); self.back_btn.set(ui, r); - ui.fill_rect(r, (*self.icons.back, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.back), r, ScaleType::Fit)); let alpha = fade_in_time().map_or(1., |tt| ((t - self.fade_start) / tt).clamp(-1., 0.) + 1.); ui.alpha::>(alpha, |ui| { @@ -2641,7 +2642,7 @@ impl Scene for SongScene { let s = 0.25; let r = Rect::new(-0.94, ui.top - s - 0.06, s, s); let icon = self.record.as_ref().map_or(0, |it| icon_index(it.score as _, it.full_combo)); - ui.fill_rect(r, (*self.rank_icons[icon], r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.rank_icons[icon]), r, ScaleType::Fit)); let score = self.record.as_ref().map(|it| it.score).unwrap_or_default(); let accuracy = self.record.as_ref().map(|it| it.accuracy).unwrap_or_default(); let r = ui @@ -2660,7 +2661,7 @@ impl Scene for SongScene { if self.info.id.is_some() { let h = 0.09; let mut r = Rect::new(r.x, r.y - h, h, h); - ui.fill_rect(r, (*self.icons.ldb, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.ldb), r, ScaleType::Fit)); if let Some((rank, _)) = &self.ldb { ui.text(if let Some(rank) = rank { format!("#{rank}") @@ -2700,9 +2701,9 @@ impl Scene for SongScene { r, ( if self.local_path.is_some() { - *self.icons.play + Texture2D::clone(&self.icons.play) } else { - *self.icons.download + Texture2D::clone(&self.icons.download) }, r, ScaleType::Fit, @@ -2716,7 +2717,7 @@ impl Scene for SongScene { let s = 0.08; let r = Rect::new(-s, 0., s, s); let cc = semi_white(0.4); - ui.fill_rect(r, (*self.icons.menu, r, ScaleType::Fit, if self.menu_options.is_empty() { cc } else { WHITE })); + ui.fill_rect(r, (Texture2D::clone(&self.icons.menu), r, ScaleType::Fit, if self.menu_options.is_empty() { cc } else { WHITE })); self.menu_btn.set(ui, r); if self.need_show_menu { self.need_show_menu = false; @@ -2727,7 +2728,7 @@ impl Scene for SongScene { self.menu.show(ui, t, Rect::new(r.x - d, r.bottom() + 0.02, r.w + d, h)); } ui.dx(-r.w - 0.03); - ui.fill_rect(r, (*self.icons.info, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(&self.icons.info), r, ScaleType::Fit)); self.info_btn.set(ui, r); ui.dx(-r.w - 0.03); @@ -2743,7 +2744,7 @@ impl Scene for SongScene { fav }; let fav_icon = if is_fav { &self.icons.star } else { &self.icons.star_outline }; - ui.fill_rect(r, (**fav_icon, r, ScaleType::Fit)); + ui.fill_rect(r, (Texture2D::clone(fav_icon), r, ScaleType::Fit)); self.fav_btn.set(ui, r); if self.need_show_fav_menu { self.need_show_fav_menu = false; @@ -2755,11 +2756,11 @@ impl Scene for SongScene { } ui.dx(-r.w - 0.03); - ui.fill_rect(r, (*self.icons.edit, r, ScaleType::Fit, if self.local_path.is_some() { WHITE } else { cc })); + ui.fill_rect(r, (Texture2D::clone(&self.icons.edit), r, ScaleType::Fit, if self.local_path.is_some() { WHITE } else { cc })); self.edit_btn.set(ui, r); ui.dx(-r.w - 0.03); } - ui.fill_rect(r, (*self.icons.r#mod, r, ScaleType::Fit, if self.local_path.is_some() { WHITE } else { cc })); + ui.fill_rect(r, (Texture2D::clone(&self.icons.r#mod), r, ScaleType::Fit, if self.local_path.is_some() { WHITE } else { cc })); self.mod_btn.set(ui, r); }); @@ -2839,7 +2840,7 @@ impl Scene for SongScene { let mut r = ui.screen_rect(); r.y += r.h * (1. - p); rect_shadow(r, 0.01, 0.5); - ui.fill_rect(r, (**self.background.lock().unwrap().as_ref().unwrap(), r)); + ui.fill_rect(r, (self.background.lock().unwrap().as_ref().unwrap().deref().clone(), r)); ui.fill_rect(r, semi_black(0.3)); } diff --git a/phira/src/scene/unlock.rs b/phira/src/scene/unlock.rs index d3040a71..b64b1fff 100644 --- a/phira/src/scene/unlock.rs +++ b/phira/src/scene/unlock.rs @@ -184,9 +184,9 @@ impl Scene for UnlockScene { fn render(&mut self, tm: &mut TimeManager, ui: &mut prpr::ui::Ui) -> Result<()> { let mut cam = ui.camera(); - let asp = -cam.zoom.y; + let asp = cam.zoom.y; let t = tm.now(); - cam.render_target = self.render_target; + cam.render_target = self.render_target.clone(); set_camera(&cam); clear_background(BLACK); @@ -230,7 +230,7 @@ impl Scene for UnlockScene { } else { let alpha = if t < 0.5 { t as f32 / 0.5 * 0.3 } else { 0.3 }; let r = ui.screen_rect(); - ui.fill_rect(r, (*self.background, r)); + ui.fill_rect(r, (Texture2D::clone(&self.background), r)); ui.fill_rect(r, semi_black(alpha)); } } diff --git a/phira/src/uml.rs b/phira/src/uml.rs index 01bed609..323f0b7b 100644 --- a/phira/src/uml.rs +++ b/phira/src/uml.rs @@ -217,7 +217,7 @@ impl Element for Image { } let r = c.r.eval(uml)?.rect()?; if let Some(tex) = self.tex.borrow().as_ref() { - ui.fill_rect(r, (**tex, r, c.t, c.c.0)); + ui.fill_rect(r, (Texture2D::clone(tex), r, c.t, c.c.0)); } Ok(Var::Rect(r)) } diff --git a/prpr/Cargo.toml b/prpr/Cargo.toml index 38a46976..09b28e43 100644 --- a/prpr/Cargo.toml +++ b/prpr/Cargo.toml @@ -76,7 +76,6 @@ uuid = { workspace = true, features = ["v4"] } zip = { workspace = true, default-features = false, features = ["deflate"] } macroquad = { workspace = true, default-features = false } -miniquad = { workspace = true } prpr-avc = { workspace = true, optional = true } prpr-l10n = { workspace = true } diff --git a/prpr/locales/de-DE/input.ftl b/prpr/locales/de-DE/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/en-US/input.ftl b/prpr/locales/en-US/input.ftl new file mode 100644 index 00000000..0eca0802 --- /dev/null +++ b/prpr/locales/en-US/input.ftl @@ -0,0 +1,4 @@ +select-all = Select All +copy = Copy +cut = Cut +paste = Paste diff --git a/prpr/locales/fr-FR/input.ftl b/prpr/locales/fr-FR/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/id-ID/input.ftl b/prpr/locales/id-ID/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/ja-JP/input.ftl b/prpr/locales/ja-JP/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/ko-KR/input.ftl b/prpr/locales/ko-KR/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/mn-MN/input.ftl b/prpr/locales/mn-MN/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/pl-PL/input.ftl b/prpr/locales/pl-PL/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/pt-BR/input.ftl b/prpr/locales/pt-BR/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/ru-RU/input.ftl b/prpr/locales/ru-RU/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/th-TH/input.ftl b/prpr/locales/th-TH/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/tr-TR/input.ftl b/prpr/locales/tr-TR/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/vi-VN/input.ftl b/prpr/locales/vi-VN/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/locales/zh-CN/input.ftl b/prpr/locales/zh-CN/input.ftl new file mode 100644 index 00000000..5b1b67e0 --- /dev/null +++ b/prpr/locales/zh-CN/input.ftl @@ -0,0 +1,4 @@ +select-all = 全选 +copy = 复制 +cut = 剪切 +paste = 粘贴 diff --git a/prpr/locales/zh-TW/input.ftl b/prpr/locales/zh-TW/input.ftl new file mode 100644 index 00000000..e69de29b diff --git a/prpr/src/core/chart.rs b/prpr/src/core/chart.rs index 898db21f..23e80e2b 100644 --- a/prpr/src/core/chart.rs +++ b/prpr/src/core/chart.rs @@ -169,18 +169,6 @@ impl Chart { target.blit(); } } - if !res.no_effect { - let render = |res: &mut Resource| { - for effect in &self.extra.effects { - effect.render(res); - } - }; - if res.config.flip_x() { - res.apply_model_of(&Matrix::identity().append_nonuniform_scaling(&Vector::new(-1., 1.)), render); - } else { - render(res); - } - } }); } } diff --git a/prpr/src/core/effect.rs b/prpr/src/core/effect.rs index 7c03e86a..9b445c8d 100644 --- a/prpr/src/core/effect.rs +++ b/prpr/src/core/effect.rs @@ -112,7 +112,7 @@ impl Effect { let mut new_uniforms = Vec::new(); let mut add_uniform = |(name, its_type): (String, UniformType)| { if ocurred_uniforms.insert(name.clone()) { - new_uniforms.push((name, its_type)); + new_uniforms.push(UniformDesc::new(&name, its_type)); } }; for def in &defaults { @@ -129,8 +129,7 @@ impl Effect { t: f64::NEG_INFINITY, defaults, material: load_material( - VERTEX_SHADER, - shader, + ShaderSource::Glsl { vertex: VERTEX_SHADER, fragment: shader }, MaterialParams { uniforms: new_uniforms, textures: vec!["screenTexture".to_owned()], @@ -169,15 +168,15 @@ impl Effect { let target = res.chart_target.as_mut().unwrap(); target.swap(); let tex = target.old().texture; - self.material.set_texture("screenTexture", tex); + self.material.set_texture("screenTexture", tex.clone()); let screen_dim = vec2(tex.width(), tex.height()); self.material.set_uniform("screenSize", screen_dim); - gl.quad_gl.render_pass(Some(target.output().render_pass)); + gl.quad_gl.render_pass(Some(target.output().render_pass.raw_miniquad_id())); let vp = get_viewport(); self.material.set_uniform("UVScale", vec2(vp.2 as _, vp.3 as _) / screen_dim); - gl_use_material(self.material); + gl_use_material(&self.material); let top = 1. / if self.global { screen_aspect() } else { res.aspect_ratio }; draw_rectangle(-1., -top, 2., top * 2., WHITE); gl_use_default_material(); @@ -186,7 +185,6 @@ impl Effect { impl Drop for Effect { fn drop(&mut self) { - self.material.delete(); } } diff --git a/prpr/src/core/line.rs b/prpr/src/core/line.rs index b2c19154..4f25ccd5 100644 --- a/prpr/src/core/line.rs +++ b/prpr/src/core/line.rs @@ -5,7 +5,7 @@ use crate::{ ui::Ui, }; use macroquad::prelude::*; -use miniquad::{RenderPass, Texture, TextureParams, TextureWrap}; +use macroquad::miniquad::{self, RenderPass, TextureParams, TextureWrap}; use nalgebra::Rotation2; use serde::Deserialize; use std::cell::RefCell; @@ -259,7 +259,7 @@ impl JudgeLine { } let hf = vec2(texture.width(), texture.height()); draw_texture_ex( - **texture, + texture, -hf.x / 2., -hf.y / 2., color, @@ -277,7 +277,7 @@ impl JudgeLine { color.a = alpha.max(0.0); let hf = vec2(frame.width(), frame.height()); draw_texture_ex( - **frame, + frame, -hf.x / 2., -hf.y / 2., color, @@ -304,17 +304,18 @@ impl JudgeLine { let vp = get_viewport(); let pass = *guard.0.get_or_insert_with(|| { let ctx = &mut gl.quad_context; - let tex = Texture::new_render_texture( - ctx, + let tex = ctx.new_render_texture( TextureParams { width: vp.2 as _, height: vp.3 as _, format: miniquad::TextureFormat::RGBA8, - filter: FilterMode::Linear, + min_filter: FilterMode::Linear, + mag_filter: FilterMode::Linear, wrap: TextureWrap::Clamp, + ..Default::default() }, ); - RenderPass::new(ctx, tex, None) + ctx.new_render_pass(tex, None) }); gl.flush(); let old_pass = gl.quad_gl.get_active_render_pass(); @@ -340,10 +341,10 @@ impl JudgeLine { let guard = state.borrow_mut(); if guard.1 { let ctx = unsafe { get_internal_gl() }.quad_context; - let tex = guard.0.as_ref().unwrap().texture(ctx); + let tex = ctx.render_pass_texture(*guard.0.as_ref().unwrap()); let top = 1. / res.aspect_ratio; draw_texture_ex( - Texture2D::from_miniquad_texture(tex), + &Texture2D::from_miniquad_texture(tex), -1., -top, WHITE, diff --git a/prpr/src/core/note.rs b/prpr/src/core/note.rs index 06615ca6..a06da8a3 100644 --- a/prpr/src/core/note.rs +++ b/prpr/src/core/note.rs @@ -108,7 +108,7 @@ fn draw_tex_pts(res: &Resource, texture: Texture2D, order: i8, p: [Point; 4], co ]; res.note_buffer .borrow_mut() - .push((order, texture.raw_miniquad_texture_handle().gl_internal_id()), vertices); + .push((order, match unsafe { get_internal_gl().quad_context.texture_raw_id(texture.raw_miniquad_id()) } { miniquad::RawId::OpenGl(id) => id }), vertices); } fn draw_center(res: &Resource, tex: Texture2D, order: i8, scale: f32, color: Color) { @@ -265,7 +265,7 @@ impl Note { }; match self.kind { NoteKind::Click => { - draw(res, *style.click); + draw(res, (*style.click).clone()); } NoteKind::Hold { end_time, end_height } => { res.with_model(self.now_transform(res, ctrl_obj, 0., 0.), |res| { @@ -293,7 +293,7 @@ impl Note { // TODO (end_height - height) is not always total height draw_tex( res, - **(if res.res_pack.info.hold_repeat { + Texture2D::clone(if res.res_pack.info.hold_repeat { style.hold_body.as_ref().unwrap() } else { tex @@ -324,10 +324,10 @@ impl Note { let hf = vec2(scale, r.h / r.w * scale * ratio); draw_tex( res, - **tex, - order, - -scale, - bottom - if res.res_pack.info.hold_compact { hf.y } else { hf.y * 2. }, + Texture2D::clone(tex), + order, + -scale, + bottom - if res.res_pack.info.hold_compact { hf.y } else { hf.y * 2. }, color, DrawTextureParams { source: Some(r), @@ -342,7 +342,7 @@ impl Note { let hf = vec2(scale, r.h / r.w * scale * ratio); draw_tex( res, - **tex, + Texture2D::clone(tex), order, -scale, top - if res.res_pack.info.hold_compact { hf.y } else { 0. }, @@ -357,10 +357,10 @@ impl Note { }); } NoteKind::Flick => { - draw(res, *style.flick); + draw(res, (*style.flick).clone()); } NoteKind::Drag => { - draw(res, *style.drag); + draw(res, (*style.drag).clone()); } } } @@ -382,9 +382,9 @@ impl BadNote { draw_center( res, match &self.kind { - NoteKind::Click => *style.click, - NoteKind::Drag => *style.drag, - NoteKind::Flick => *style.flick, + NoteKind::Click => (*style.click).clone(), + NoteKind::Drag => (*style.drag).clone(), + NoteKind::Flick => (*style.flick).clone(), _ => unreachable!(), }, self.kind.order(), diff --git a/prpr/src/core/render.rs b/prpr/src/core/render.rs index 300bcf48..ea6d5a24 100644 --- a/prpr/src/core/render.rs +++ b/prpr/src/core/render.rs @@ -2,14 +2,12 @@ use macroquad::{ texture::{RenderTarget, Texture2D}, window::get_internal_gl, }; -use miniquad::{gl::GLuint, RenderPass, Texture, TextureFormat}; +use macroquad::miniquad::{self as miniquad, gl::GLuint}; -// TODO: doc pub struct MSRenderTarget { dim: (u32, u32), fbo: GLuint, - rbo: GLuint, - dummy: RenderTarget, + input: RenderTarget, output: [Option; 2], } @@ -24,101 +22,106 @@ pub fn copy_fbo(src: GLuint, dst: GLuint, dim: (u32, u32)) -> bool { } } +fn get_fbo(target: &RenderTarget) -> GLuint { + let gl = unsafe { get_internal_gl() }; + let rp = target.render_pass.raw_miniquad_id(); + unsafe { + gl.quad_context.begin_pass(Some(rp), miniquad::PassAction::Nothing); + let mut fbo: GLuint = 0; + miniquad::gl::glGetIntegerv(miniquad::gl::GL_FRAMEBUFFER_BINDING, &mut fbo as *mut _ as *mut _); + gl.quad_context.end_render_pass(); + fbo + } +} + pub fn internal_id(target: RenderTarget) -> GLuint { - target.render_pass.gl_internal_id(unsafe { get_internal_gl() }.quad_context) + get_fbo(&target) +} + +fn create_render_target_rgb8(width: u32, height: u32, sample_count: i32) -> RenderTarget { + let gl = unsafe { get_internal_gl() }; + let ctx = gl.quad_context; + + let color_texture = ctx.new_render_texture(miniquad::TextureParams { + width, + height, + format: miniquad::TextureFormat::RGB8, + sample_count, + ..Default::default() + }); + + let render_pass = if sample_count > 1 { + let resolve_texture = ctx.new_render_texture(miniquad::TextureParams { + width, + height, + format: miniquad::TextureFormat::RGB8, + sample_count: 1, + ..Default::default() + }); + ctx.new_render_pass_mrt(&[color_texture], Some(&[resolve_texture]), None) + } else { + ctx.new_render_pass(color_texture, None) + }; + + // Get the texture that contains the final result (resolve texture for MSAA, color texture otherwise) + let result_texture_id = if sample_count > 1 { + ctx.render_pass_color_attachments(render_pass)[0] + } else { + color_texture + }; + + RenderTarget { + texture: Texture2D::from_miniquad_texture(result_texture_id), + render_pass: macroquad::texture::RenderPass { + color_texture: Texture2D::from_miniquad_texture(result_texture_id), + depth_texture: None, + render_pass: std::sync::Arc::new(render_pass), + }, + } } impl MSRenderTarget { pub fn new(dim: (u32, u32), samples: u32) -> Self { - let mut fbo = 0; - let mut rbo = 0; - unsafe { - use miniquad::gl::*; - glGenRenderbuffers(1, &mut rbo as *mut _); - glBindRenderbuffer(GL_RENDERBUFFER, rbo); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples as _, GL_RGB8, dim.0 as _, dim.1 as _); - glGenFramebuffers(1, &mut fbo as *mut _); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); - } - let gl = unsafe { get_internal_gl() }; - let texture = Texture::new_render_texture( - gl.quad_context, - miniquad::TextureParams { - width: dim.0, - height: dim.1, - format: TextureFormat::RGB8, - ..Default::default() - }, - ); - let render_pass = RenderPass::new(gl.quad_context, texture, None); - let dummy_render_pass = RenderPass::from_raw(gl.quad_context, fbo, texture); + let input = create_render_target_rgb8(dim.0, dim.1, samples as i32); + let output = create_render_target_rgb8(dim.0, dim.1, 1); + let fbo = get_fbo(&input); Self { dim, fbo, - rbo, - dummy: RenderTarget { - texture: Texture2D::from_miniquad_texture(texture), - render_pass: dummy_render_pass, - }, - output: [ - Some(RenderTarget { - texture: Texture2D::from_miniquad_texture(texture), - render_pass, - }), - None, - ], + input, + output: [Some(output), None], } } pub fn blit(&self) { - copy_fbo(self.fbo, internal_id(self.output[0].unwrap()), self.dim); + if let Some(target) = &self.output[0] { + let dst_fbo = get_fbo(target); + copy_fbo(self.fbo, dst_fbo, self.dim); + } } pub fn swap(&mut self) { self.output.swap(0, 1); if self.output[0].is_none() { - let gl = unsafe { get_internal_gl() }; - let texture = miniquad::Texture::new_render_texture( - gl.quad_context, - miniquad::TextureParams { - width: self.dim.0, - height: self.dim.1, - format: TextureFormat::RGB8, - ..Default::default() - }, - ); - let render_pass = RenderPass::new(gl.quad_context, texture, None); - self.output[0] = Some(RenderTarget { - texture: Texture2D::from_miniquad_texture(texture), - render_pass, - }); - copy_fbo(internal_id(self.output[1].unwrap()), internal_id(self.output[0].unwrap()), self.dim); + self.output[0] = Some(create_render_target_rgb8(self.dim.0, self.dim.1, 1)); } } pub fn input(&self) -> RenderTarget { - self.dummy + self.input.clone() } pub fn output(&self) -> RenderTarget { - self.output[0].unwrap() + self.output[0].clone().unwrap() } pub fn old(&self) -> RenderTarget { - self.output[1].unwrap() + self.output[1].clone().unwrap() } } impl Drop for MSRenderTarget { fn drop(&mut self) { - unsafe { - use miniquad::gl::*; - glDeleteRenderbuffers(1, &self.rbo as *const _); - glDeleteFramebuffers(1, &self.fbo as *const _); - } - for target in self.output.iter().flatten() { - target.delete(); - } + // Render pass and texture cleanup is handled by macroquad's RenderPass Drop impl } } diff --git a/prpr/src/core/resource.rs b/prpr/src/core/resource.rs index 492fc70b..f8992198 100644 --- a/prpr/src/core/resource.rs +++ b/prpr/src/core/resource.rs @@ -8,9 +8,9 @@ use crate::{ }; use anyhow::{bail, Context, Result}; use macroquad::prelude::*; -use miniquad::{ +use macroquad::miniquad::{ gl::{GLuint, GL_LINEAR}, - Texture, TextureWrap, + RawId, TextureId, TextureWrap, }; use sasa::{AudioClip, AudioManager, Sfx}; use serde::Deserialize; @@ -92,9 +92,9 @@ pub struct ResPackInfo { fn parse_color_guess_alpha(c: u32) -> Color { if c > 0xffffff { - Color::from_hex_argb(c) + Color::from_hex(c) } else { - Color::from_hex_rgb(c) + Color::from_hex(c) } } @@ -246,7 +246,7 @@ impl ResourcePack { &pixels.bytes[(atlas.0 as usize * width as usize * 4)..(pixels.bytes.len() - atlas.1 as usize * width as usize * 4)], ); let context = unsafe { get_internal_gl() }.quad_context; - res.raw_miniquad_texture_handle().set_wrap(context, TextureWrap::Repeat); + context.texture_set_wrap(res.raw_miniquad_id(), TextureWrap::Repeat, TextureWrap::Repeat); style.hold_body = Some(res.into()); } get_body(&mut note_style); @@ -320,7 +320,7 @@ impl ParticleEmitter { scale: res_pack.info.hit_fx_scale, emitter: Emitter::new(EmitterConfig { local_coords: false, - texture: Some(*res_pack.hit_fx), + texture: Some((*res_pack.hit_fx).clone()), lifetime: res_pack.info.hit_fx_duration, lifetime_randomness: 0.0, initial_rotation_randomness: 0.0, @@ -394,7 +394,7 @@ impl NoteBuffer { let gl = gl.quad_gl; gl.draw_mode(DrawMode::Triangles); for ((_, tex_id), meshes) in std::mem::take(&mut self.0).into_iter() { - gl.texture(Some(Texture2D::from_miniquad_texture(unsafe { Texture::from_raw_id(tex_id, miniquad::TextureFormat::RGBA8) }))); + gl.texture(Some(&Texture2D::from_miniquad_texture(TextureId::from_raw_id(RawId::OpenGl(tex_id))))); for mesh in meshes { gl.geometry(&mesh.0, &mesh.1); } @@ -504,7 +504,7 @@ impl Resource { .context("Failed to load resource pack")?; let camera = Camera2D { target: vec2(0., 0.), - zoom: vec2(1., -config.aspect_ratio.unwrap_or(info.aspect_ratio)), + zoom: vec2(1., config.aspect_ratio.unwrap_or(info.aspect_ratio)), ..Default::default() }; @@ -614,7 +614,7 @@ impl Resource { self.camera.viewport = Some(viewport(aspect_ratio, vp)); } else { self.aspect_ratio = aspect_ratio.min(vp.2 as f32 / vp.3 as f32); - self.camera.zoom.y = -self.aspect_ratio; + self.camera.zoom.y = self.aspect_ratio; self.camera.viewport = Some(viewport(self.aspect_ratio, vp)); }; true diff --git a/prpr/src/core/shaders/chromatic.glsl b/prpr/src/core/shaders/chromatic.glsl index f12df6cf..10f606a1 100644 --- a/prpr/src/core/shaders/chromatic.glsl +++ b/prpr/src/core/shaders/chromatic.glsl @@ -27,5 +27,5 @@ void main() { vec2 slice_offset = (t - 1.0) * power * offset; c += slice * texture2D(screenTexture, uv + slice_offset).rgb; } - gl_FragColor.rgb = c / sum; + gl_FragColor = vec4(c / sum, 1.0); } diff --git a/prpr/src/core/shaders/radial_blur.glsl b/prpr/src/core/shaders/radial_blur.glsl index 5cbdb4e1..85c52a5b 100644 --- a/prpr/src/core/shaders/radial_blur.glsl +++ b/prpr/src/core/shaders/radial_blur.glsl @@ -14,10 +14,9 @@ void main() { vec2 direction = uv - vec2(centerX, centerY); vec3 c = vec3(0.0); float f = 1.0 / sampleCount; - vec2 screen_uv = uv / 2.0 + vec2(0.5, 0.5); for (float i = 0.0; i < 64.0; ++i) { if (i >= sampleCount) break; c += texture2D(screenTexture, uv - power * direction * i).rgb * f; } - gl_FragColor.rgb = c; + gl_FragColor = vec4(c, 1.0); } diff --git a/prpr/src/core/video.rs b/prpr/src/core/video.rs index 4da1f512..03507f0c 100644 --- a/prpr/src/core/video.rs +++ b/prpr/src/core/video.rs @@ -2,7 +2,7 @@ use super::Anim; use crate::ext::{source_of_image, ScaleType}; use anyhow::Result; use macroquad::prelude::*; -use miniquad::{Texture, TextureFormat, TextureParams, TextureWrap}; +use macroquad::miniquad::{TextureFormat, TextureParams, TextureWrap}; use prpr_avc::AVPixelFormat; use serde::Deserialize; use std::{cell::RefCell, io::Write}; @@ -35,14 +35,15 @@ pub struct Video { } fn new_tex(w: u32, h: u32) -> Texture2D { - Texture2D::from_miniquad_texture(Texture::new_render_texture( - unsafe { get_internal_gl() }.quad_context, + Texture2D::from_miniquad_texture(unsafe { get_internal_gl() }.quad_context.new_render_texture( TextureParams { width: w, height: h, format: TextureFormat::Alpha, - filter: FilterMode::Linear, + min_filter: FilterMode::Linear, + mag_filter: FilterMode::Linear, wrap: TextureWrap::Clamp, + ..Default::default() }, )) } @@ -59,8 +60,7 @@ impl Video { let h = format.height as u32; let material = load_material( - shader::VERTEX, - shader::FRAGMENT, + ShaderSource::Glsl { vertex: shader::VERTEX, fragment: shader::FRAGMENT }, MaterialParams { pipeline_params: PipelineParams::default(), uniforms: Vec::new(), @@ -70,9 +70,9 @@ impl Video { let tex_y = new_tex(w, h); let tex_u = new_tex(w / 2, h / 2); let tex_v = new_tex(w / 2, h / 2); - material.set_texture("tex_y", tex_y); - material.set_texture("tex_u", tex_u); - material.set_texture("tex_v", tex_v); + material.set_texture("tex_y", tex_y.clone()); + material.set_texture("tex_u", tex_u.clone()); + material.set_texture("tex_v", tex_v.clone()); Ok(Self { video, @@ -122,9 +122,9 @@ impl Video { frame.get_data_half(2, &mut buf[2]); let ctx = unsafe { get_internal_gl() }.quad_context; - self.tex_y.raw_miniquad_texture_handle().update(ctx, &buf[0]); - self.tex_u.raw_miniquad_texture_handle().update(ctx, &buf[1]); - self.tex_v.raw_miniquad_texture_handle().update(ctx, &buf[2]); + ctx.texture_update(self.tex_y.raw_miniquad_id(), &buf[0]); + ctx.texture_update(self.tex_u.raw_miniquad_id(), &buf[1]); + ctx.texture_update(self.tex_v.raw_miniquad_id(), &buf[2]); }); }); @@ -135,7 +135,7 @@ impl Video { if !(0f64..self.duration).contains(&(t - self.start_time)) { return; } - gl_use_material(self.material); + gl_use_material(&self.material); let top = 1. / aspect_ratio; let r = Rect::new(-1., -top, 2., top * 2.); let s = source_of_image(&self.tex_y, r, self.scale_type).unwrap_or_else(|| Rect::new(0., 0., 1., 1.)); diff --git a/prpr/src/ext.rs b/prpr/src/ext.rs index b03afaf7..cf0f3da9 100644 --- a/prpr/src/ext.rs +++ b/prpr/src/ext.rs @@ -86,7 +86,6 @@ impl RectExt for Rect { struct SafeTextureInner(Texture2D); impl Drop for SafeTextureInner { fn drop(&mut self) { - self.0.delete() } } @@ -94,16 +93,17 @@ pub struct SafeTexture(Arc); impl SafeTexture { pub fn into_inner(self) -> Texture2D { let arc = self.0; - let res = arc.0; + let res = arc.0.clone(); std::mem::forget(arc); res } pub fn with_mipmap(self) -> Self { - let id = self.0 .0.raw_miniquad_texture_handle().gl_internal_id(); + let id = self.0 .0.raw_miniquad_id(); + let miniquad::RawId::OpenGl(gl_id) = unsafe { get_internal_gl().quad_context.texture_raw_id(id) }; unsafe { use miniquad::gl::*; - glBindTexture(GL_TEXTURE_2D, id); + glBindTexture(GL_TEXTURE_2D, gl_id); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR as _); } @@ -111,10 +111,11 @@ impl SafeTexture { } pub fn with_filter(self, filter: GLenum) -> Self { - let id = self.0 .0.raw_miniquad_texture_handle().gl_internal_id(); + let id = self.0 .0.raw_miniquad_id(); + let miniquad::RawId::OpenGl(gl_id) = unsafe { get_internal_gl().quad_context.texture_raw_id(id) }; unsafe { use miniquad::gl::*; - glBindTexture(GL_TEXTURE_2D, id); + glBindTexture(GL_TEXTURE_2D, gl_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter as _); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter as _); } @@ -171,17 +172,7 @@ pub fn nalgebra_to_glm(mat: &Matrix) -> Mat4 { pub fn get_viewport() -> (i32, i32, i32, i32) { let gl = unsafe { get_internal_gl() }; - gl.quad_gl.get_viewport().unwrap_or_else(|| { - let (w, h) = gl - .quad_gl - .get_active_render_pass() - .map(|it| { - let tex = it.texture(gl.quad_context); - (tex.width as i32, tex.height as i32) - }) - .unwrap_or_else(|| (screen_width() as _, screen_height() as _)); - (0, 0, w, h) - }) + gl.quad_gl.get_viewport() } #[inline] @@ -226,8 +217,8 @@ pub fn source_of_image(tex: &Texture2D, rect: Rect, scale_type: ScaleType) -> Op } } -pub fn draw_image(tex: Texture2D, rect: Rect, scale_type: ScaleType) { - let source = source_of_image(&tex, rect, scale_type); +pub fn draw_image(tex: &Texture2D, rect: Rect, scale_type: ScaleType) { + let source = source_of_image(tex, rect, scale_type); let (w, h) = (tex.width(), tex.height()); draw_texture_ex( tex, @@ -259,7 +250,7 @@ pub fn draw_parallelogram_ex(rect: Rect, texture: Option<(Texture2D, Rect)>, top ]; let v = if let Some((tex, tex_rect)) = texture { let lt = tex_rect.h * tex.height() * PARALLELOGRAM_SLOPE / tex.width(); - gl.texture(Some(tex)); + gl.texture(Some(&tex)); [ Vertex::new(p[0].x, p[0].y, 0., tex_rect.x + lt, tex_rect.y, top), Vertex::new(p[1].x, p[1].y, 0., tex_rect.right(), tex_rect.y, top), @@ -426,8 +417,7 @@ pub fn make_pipeline(write_color: bool, pass_op: StencilOp, test_func: CompareFu } = unsafe { get_internal_gl() }; gl.make_pipeline( context, - shader::VERTEX, - shader::FRAGMENT, + ShaderSource::Glsl { vertex: shader::VERTEX, fragment: shader::FRAGMENT }, PipelineParams { color_write: (write_color, write_color, write_color, write_color), color_blend: Some(BlendState::new( diff --git a/prpr/src/judge.rs b/prpr/src/judge.rs index ecb84f4c..cdadc11a 100644 --- a/prpr/src/judge.rs +++ b/prpr/src/judge.rs @@ -9,11 +9,11 @@ use macroquad::prelude::{ utils::{register_input_subscriber, repeat_all_miniquad_input}, *, }; -use miniquad::{EventHandler, MouseButton}; +use macroquad::miniquad::{EventHandler, KeyMods, MouseButton, TouchPhase as MiniquadTouchPhase}; use once_cell::sync::Lazy; use sasa::{PlaySfxParams, Sfx}; use serde::Serialize; -use std::{cell::RefCell, collections::HashMap, mem, num::FpCategory}; +use std::{cell::RefCell, collections::HashMap, mem, num::FpCategory, time::Instant}; use tracing::debug; pub const FLICK_SPEED_THRESHOLD: f32 = 0.8; @@ -25,6 +25,8 @@ pub const DIST_FACTOR: f64 = 0.2; const EARLY_OFFSET: f64 = 0.07; +static START_TIME: Lazy = Lazy::new(Instant::now); + #[derive(Debug, Clone)] pub enum HitSound { None, @@ -83,7 +85,7 @@ fn get_uptime() -> f64 { #[cfg(target_os = "windows")] fn get_uptime() -> f64 { - miniquad::native::windows::get_uptime() + START_TIME.elapsed().as_secs_f64() } pub struct FlickTracker { @@ -975,9 +977,9 @@ fn button_to_id(button: MouseButton) -> u64 { } impl EventHandler for Handler { - fn update(&mut self, _: &mut miniquad::Context) {} - fn draw(&mut self, _: &mut miniquad::Context) {} - fn touch_event(&mut self, _: &mut miniquad::Context, phase: miniquad::TouchPhase, id: u64, x: f32, y: f32, time: f64) { + fn update(&mut self) {} + fn draw(&mut self) {} + fn touch_event(&mut self, phase: MiniquadTouchPhase, id: u64, x: f32, y: f32, time: f64) { self.status.touches.push(Touch { id, phase: phase.into(), @@ -986,12 +988,12 @@ impl EventHandler for Handler { }); } - fn mouse_wheel_event(&mut self, _ctx: &mut miniquad::Context, x: f32, y: f32) { + fn mouse_wheel_event(&mut self, x: f32, y: f32) { self.wheel.0 += x; self.wheel.1 += y; } - fn mouse_button_down_event(&mut self, _ctx: &mut miniquad::Context, button: MouseButton, x: f32, y: f32) { + fn mouse_button_down_event(&mut self, button: MouseButton, x: f32, y: f32) { self.status.touches.push(Touch { id: button_to_id(button), phase: TouchPhase::Started, @@ -1000,7 +1002,7 @@ impl EventHandler for Handler { }); } - fn mouse_button_up_event(&mut self, _ctx: &mut miniquad::Context, button: MouseButton, x: f32, y: f32) { + fn mouse_button_up_event(&mut self, button: MouseButton, x: f32, y: f32) { self.status.touches.push(Touch { id: button_to_id(button), phase: TouchPhase::Ended, @@ -1009,14 +1011,14 @@ impl EventHandler for Handler { }); } - fn key_down_event(&mut self, _ctx: &mut miniquad::Context, _keycode: KeyCode, _keymods: miniquad::KeyMods, repeat: bool) { + fn key_down_event(&mut self, _keycode: KeyCode, _keymods: KeyMods, repeat: bool) { if !repeat { self.status.key_delta += 1; self.status.keys_down += 1; } } - fn key_up_event(&mut self, _ctx: &mut miniquad::Context, _keycode: KeyCode, _keymods: miniquad::KeyMods) { + fn key_up_event(&mut self, _keycode: KeyCode, _keymods: KeyMods) { self.status.key_delta -= 1; } } diff --git a/prpr/src/log.rs b/prpr/src/log.rs index 530907ea..f9703f0f 100644 --- a/prpr/src/log.rs +++ b/prpr/src/log.rs @@ -1,7 +1,7 @@ //! Logging utilities. use colored::Colorize; -use miniquad::{debug, error, info, trace, warn}; +use macroquad::miniquad::{debug, error, info, trace, warn}; use tracing::{field::Visit, Level, Subscriber}; use tracing_subscriber::{prelude::*, EnvFilter, Layer}; diff --git a/prpr/src/parse/rpe.rs b/prpr/src/parse/rpe.rs index a13648b4..867579ea 100644 --- a/prpr/src/parse/rpe.rs +++ b/prpr/src/parse/rpe.rs @@ -1,6 +1,7 @@ use anyhow::{Context, Result}; use image::{codecs::gif, AnimationDecoder, DynamicImage, ImageError}; use macroquad::prelude::{Color, WHITE}; +use macroquad::window::get_internal_gl; use sasa::AudioClip; use serde::{Deserialize, Deserializer}; use std::{any::Any, cell::RefCell, collections::HashMap, future::IntoFuture, io::Cursor, rc::Rc, str::FromStr, time::Duration}; @@ -773,7 +774,7 @@ async fn parse_judge_line( let events = parse_gif_events(r, events, bezier_map, &frames).with_context(|| ptl!("gif-events-parse-failed"))?; JudgeLineKind::TextureGif(events, frames, rpe.texture.clone()) } else if let Some(texture) = line_texture_map.get(&rpe.texture) { - debug!("texture {} reused, id: {}", rpe.texture.clone(), texture.clone().into_inner().raw_miniquad_texture_handle().gl_internal_id()); + debug!("texture {} reused, id: {:?}", rpe.texture.clone(), unsafe { get_internal_gl().quad_context.texture_raw_id(texture.clone().into_inner().raw_miniquad_id()) }); JudgeLineKind::Texture(texture.clone(), rpe.texture.clone()) } else { let texture = SafeTexture::from(image::load_from_memory( @@ -786,7 +787,7 @@ async fn parse_judge_line( JudgeLineKind::Texture(texture, rpe.texture.clone()) } } else if let Some(texture) = line_texture_map.get(&rpe.texture) { - debug!("texture {} reused, id: {}", rpe.texture.clone(), texture.clone().into_inner().raw_miniquad_texture_handle().gl_internal_id()); + debug!("texture {} reused, id: {:?}", rpe.texture.clone(), unsafe { get_internal_gl().quad_context.texture_raw_id(texture.clone().into_inner().raw_miniquad_id()) }); JudgeLineKind::Texture(texture.clone(), rpe.texture.clone()) } else { let texture = SafeTexture::from(image::load_from_memory( diff --git a/prpr/src/particle.rs b/prpr/src/particle.rs index f6d47db9..2f4e9f62 100644 --- a/prpr/src/particle.rs +++ b/prpr/src/particle.rs @@ -11,6 +11,7 @@ use macroquad::prelude::*; use macroquad::window::miniquad::*; +use macroquad::miniquad::RenderPass; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Interpolation { @@ -219,7 +220,7 @@ pub enum ParticleShape { } impl ParticleShape { - fn build_bindings(&self, ctx: &mut miniquad::Context, positions_vertex_buffer: Buffer, texture: Option) -> Bindings { + fn build_bindings(&self, ctx: &mut dyn RenderingBackend, positions_vertex_buffer: BufferId, texture: Option) -> Bindings { let (geometry_vertex_buffer, index_buffer) = match self { ParticleShape::Rectangle { aspect_ratio } => { #[rustfmt::skip] @@ -231,13 +232,13 @@ impl ParticleShape { -1.0 * aspect_ratio, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, ]; - let vertex_buffer = Buffer::immutable(ctx, BufferType::VertexBuffer, vertices); + let vertex_buffer = ctx.new_buffer(BufferType::VertexBuffer, BufferUsage::Immutable, BufferSource::slice(vertices)); #[rustfmt::skip] let indices: &[u16] = &[ 0, 1, 2, 0, 2, 3 ]; - let index_buffer = Buffer::immutable(ctx, BufferType::IndexBuffer, indices); + let index_buffer = ctx.new_buffer(BufferType::IndexBuffer, BufferUsage::Immutable, BufferSource::slice(indices)); (vertex_buffer, index_buffer) } @@ -257,13 +258,13 @@ impl ParticleShape { } } - let vertex_buffer = Buffer::immutable(ctx, BufferType::VertexBuffer, &vertices); - let index_buffer = Buffer::immutable(ctx, BufferType::IndexBuffer, &indices); + let vertex_buffer = ctx.new_buffer(BufferType::VertexBuffer, BufferUsage::Immutable, BufferSource::slice(&vertices)); + let index_buffer = ctx.new_buffer(BufferType::IndexBuffer, BufferUsage::Immutable, BufferSource::slice(&indices)); (vertex_buffer, index_buffer) } ParticleShape::CustomMesh { vertices, indices } => { - let vertex_buffer = Buffer::immutable(ctx, BufferType::VertexBuffer, vertices); - let index_buffer = Buffer::immutable(ctx, BufferType::IndexBuffer, indices); + let vertex_buffer = ctx.new_buffer(BufferType::VertexBuffer, BufferUsage::Immutable, BufferSource::slice(vertices)); + let index_buffer = ctx.new_buffer(BufferType::IndexBuffer, BufferUsage::Immutable, BufferSource::slice(indices)); (vertex_buffer, index_buffer) } }; @@ -272,7 +273,7 @@ impl ParticleShape { vertex_buffers: vec![geometry_vertex_buffer, positions_vertex_buffer], index_buffer, images: vec![ - texture.map_or_else(|| Texture::from_rgba8(ctx, 1, 1, &[255, 255, 255, 255]), |texture| texture.raw_miniquad_texture_handle()) + texture.map_or_else(|| ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]), |texture| texture.raw_miniquad_id()) ], } } @@ -400,6 +401,7 @@ struct CpuParticle { pub struct Emitter { pipeline: Pipeline, + shader: ShaderId, bindings: Bindings, post_processing_pass: RenderPass, post_processing_pipeline: Pipeline, @@ -429,9 +431,10 @@ impl Emitter { let InternalGlContext { quad_context: ctx, .. } = unsafe { get_internal_gl() }; // empty, dynamic instance-data vertex buffer - let positions_vertex_buffer = Buffer::stream(ctx, BufferType::VertexBuffer, Self::MAX_PARTICLES * std::mem::size_of::()); + let zeroed = vec![0u8; Self::MAX_PARTICLES * std::mem::size_of::()]; + let positions_vertex_buffer = ctx.new_buffer(BufferType::VertexBuffer, BufferUsage::Stream, BufferSource::slice(&zeroed)); - let bindings = config.shape.build_bindings(ctx, positions_vertex_buffer, config.texture); + let bindings = config.shape.build_bindings(ctx, positions_vertex_buffer, config.texture.clone()); let (vertex, fragment) = config .material @@ -448,12 +451,11 @@ impl Emitter { let vertex = preprocess_shader(vertex, &config); let fragment = preprocess_shader(fragment, &config); - Shader::new(ctx, &vertex, &fragment, shader::meta()).unwrap() + ctx.new_shader(ShaderSource::Glsl { vertex: &vertex, fragment: &fragment }, shader::meta()).unwrap() }; let blend_mode = config.blend_mode.blend_state(); - let pipeline = Pipeline::with_params( - ctx, + let pipeline = ctx.new_pipeline( &[ BufferLayout::default(), BufferLayout { @@ -479,10 +481,9 @@ impl Emitter { ); let post_processing_shader = - Shader::new(ctx, post_processing_shader::VERTEX, post_processing_shader::FRAGMENT, post_processing_shader::meta()).unwrap(); + ctx.new_shader(ShaderSource::Glsl { vertex: post_processing_shader::VERTEX, fragment: post_processing_shader::FRAGMENT }, post_processing_shader::meta()).unwrap(); - let post_processing_pipeline = Pipeline::with_params( - ctx, + let post_processing_pipeline = ctx.new_pipeline( &[BufferLayout::default(), BufferLayout::default()], &[ VertexAttribute::with_buffer("pos", VertexFormat::Float2, 0), @@ -499,18 +500,18 @@ impl Emitter { }, ); let post_processing_pass = { - let color_img = Texture::new_render_texture( - ctx, + let color_img = ctx.new_render_texture( TextureParams { width: 320, height: 200, format: TextureFormat::RGBA8, + min_filter: FilterMode::Nearest, + mag_filter: FilterMode::Nearest, ..Default::default() }, ); - color_img.set_filter(ctx, FilterMode::Nearest); - RenderPass::new(ctx, color_img, None) + ctx.new_render_pass(color_img, None) }; let post_processing_bindings = { @@ -523,23 +524,24 @@ impl Emitter { -1.0, 1.0, 0.0, 1.0, ]; - let vertex_buffer = Buffer::immutable(ctx, BufferType::VertexBuffer, vertices); + let vertex_buffer = ctx.new_buffer(BufferType::VertexBuffer, BufferUsage::Immutable, BufferSource::slice(vertices)); #[rustfmt::skip] let indices: &[u16] = &[ 0, 1, 2, 0, 2, 3 ]; - let index_buffer = Buffer::immutable(ctx, BufferType::IndexBuffer, indices); + let index_buffer = ctx.new_buffer(BufferType::IndexBuffer, BufferUsage::Immutable, BufferSource::slice(indices)); Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, - images: vec![post_processing_pass.texture(ctx)], + images: vec![ctx.render_pass_texture(post_processing_pass)], } }; Emitter { blend_mode: config.blend_mode, batched_size_curve: config.size_curve.as_ref().map(|curve| curve.batch()), + shader, post_processing_pass, post_processing_pipeline, post_processing_bindings, @@ -623,7 +625,7 @@ impl Emitter { self.bindings = self .config .shape - .build_bindings(ctx, self.bindings.vertex_buffers[1], self.config.texture); + .build_bindings(ctx, self.bindings.vertex_buffers[1], self.config.texture.clone()); self.mesh_dirty = false; } if self.config.emitting { @@ -714,7 +716,7 @@ impl Emitter { } } - self.bindings.vertex_buffers[1].update(ctx, &self.gpu_particles[..]); + ctx.buffer_update(self.bindings.vertex_buffers[1], BufferSource::slice(&self.gpu_particles[..])); } /// Immediately emit N particles, ignoring "emitting" and "amount" params of EmitterConfig @@ -727,37 +729,59 @@ impl Emitter { fn perform_render_pass(&mut self, quad_gl: &QuadGl, ctx: &mut Context) { ctx.apply_bindings(&self.bindings); - ctx.apply_uniforms(&shader::Uniforms { + ctx.apply_uniforms(UniformsSource::table(&shader::Uniforms { mvp: quad_gl.get_projection_matrix(), emitter_position: vec3(self.position.x, self.position.y, 0.0), local_coords: if self.config.local_coords { 1.0 } else { 0.0 }, - }); + })); - ctx.draw(0, self.bindings.index_buffer.size() as i32 / std::mem::size_of::() as i32, self.gpu_particles.len() as i32); + let idx_size = ctx.buffer_size(self.bindings.index_buffer); + ctx.draw(0, idx_size as i32 / std::mem::size_of::() as i32, self.gpu_particles.len() as i32); } pub fn setup_render_pass(&mut self, quad_gl: &QuadGl, ctx: &mut Context) { if self.config.blend_mode != self.blend_mode { - self.pipeline.set_blend(ctx, Some(self.config.blend_mode.blend_state())); + self.pipeline = ctx.new_pipeline( + &[ + BufferLayout::default(), + BufferLayout { + step_func: VertexStep::PerInstance, + ..Default::default() + }, + ], + &[ + VertexAttribute::with_buffer("in_attr_pos", VertexFormat::Float3, 0), + VertexAttribute::with_buffer("in_attr_uv", VertexFormat::Float2, 0), + VertexAttribute::with_buffer("in_attr_color", VertexFormat::Float4, 0), + VertexAttribute::with_buffer("in_attr_inst_pos", VertexFormat::Float4, 1), + VertexAttribute::with_buffer("in_attr_inst_uv", VertexFormat::Float4, 1), + VertexAttribute::with_buffer("in_attr_inst_data", VertexFormat::Float4, 1), + VertexAttribute::with_buffer("in_attr_inst_color", VertexFormat::Float4, 1), + ], + self.shader, + PipelineParams { + color_blend: Some(self.config.blend_mode.blend_state()), + alpha_blend: Some(BlendState::new(Equation::Add, BlendFactor::Zero, BlendFactor::One)), + ..Default::default() + }, + ); self.blend_mode = self.config.blend_mode; } if self.config.post_processing.is_none() { let pass = quad_gl.get_active_render_pass(); if let Some(pass) = pass { - ctx.begin_pass(pass, PassAction::Nothing); + ctx.begin_pass(Some(pass), PassAction::Nothing); } else { ctx.begin_default_pass(PassAction::Nothing); } } else { - ctx.begin_pass(self.post_processing_pass, PassAction::clear_color(0.0, 0.0, 0.0, 0.0)); + ctx.begin_pass(Some(self.post_processing_pass), PassAction::clear_color(0.0, 0.0, 0.0, 0.0)); }; ctx.apply_pipeline(&self.pipeline); // This is made - let (x, y, w, h) = quad_gl - .get_viewport() - .unwrap_or_else(|| (0, 0, screen_width() as _, screen_height() as _)); + let (x, y, w, h) = quad_gl.get_viewport(); ctx.apply_viewport(x, y, w, h); } @@ -767,15 +791,13 @@ impl Emitter { if self.config.post_processing.is_some() { let pass = quad_gl.get_active_render_pass(); if let Some(pass) = pass { - ctx.begin_pass(pass, PassAction::Nothing); + ctx.begin_pass(Some(pass), PassAction::Nothing); } else { ctx.begin_default_pass(PassAction::Nothing); } ctx.apply_pipeline(&self.post_processing_pipeline); - let (x, y, w, h) = quad_gl - .get_viewport() - .unwrap_or_else(|| (0, 0, screen_width() as _, screen_height() as _)); + let (x, y, w, h) = quad_gl.get_viewport(); ctx.apply_viewport(x, y, w, h); ctx.apply_bindings(&self.post_processing_bindings); diff --git a/prpr/src/scene.rs b/prpr/src/scene.rs index 263581d4..0803ebc2 100644 --- a/prpr/src/scene.rs +++ b/prpr/src/scene.rs @@ -348,7 +348,7 @@ pub trait RenderTargetChooser { } impl RenderTargetChooser for Option { fn choose(&mut self) -> Option { - *self + self.clone() } } impl Option> RenderTargetChooser for F { @@ -558,7 +558,7 @@ impl Main { } } -fn draw_background(tex: Texture2D) { +fn draw_background(tex: &Texture2D) { let asp = screen_aspect(); let top = 1. / asp; draw_image(tex, Rect::new(-1., -top, 2., top * 2.), ScaleType::CropCenter); diff --git a/prpr/src/scene/ending.rs b/prpr/src/scene/ending.rs index dd1a0d82..4ef16529 100644 --- a/prpr/src/scene/ending.rs +++ b/prpr/src/scene/ending.rs @@ -245,13 +245,13 @@ impl Scene for EndingScene { fn render(&mut self, tm: &mut TimeManager, ui: &mut Ui) -> Result<()> { let mut cam = ui.camera(); - let asp = -cam.zoom.y; + let asp = cam.zoom.y; let top = 1. / asp; let t = tm.now() as f32; - cam.render_target = self.target; + cam.render_target = self.target.clone(); let sr = ui.screen_rect(); set_camera(&cam); - draw_background(*self.background); + draw_background(&self.background); fn ran(t: f32, l: f32, r: f32) -> f32 { ((t - l) / (r - l)).clamp(0., 1.) @@ -307,7 +307,7 @@ impl Scene for EndingScene { let icon = &self.icons[icon_index(res.score, res.max_combo == res.num_of_notes)]; let p = ran(t, 1.7, 2.4).powi(2); let r = Rect::new(0.75, br.center().y, 0., 0.).feather(0.13 + (1. - p) * 0.05); - ui.fill_rect(r, (**icon, r, ScaleType::Fit, semi_white(p))); + ui.fill_rect(r, (Texture2D::clone(icon), r, ScaleType::Fit, semi_white(p))); let y = y + 0.16; let lf = -0.48 + (1.2 - y) / 1.9 * 0.4; @@ -414,12 +414,12 @@ impl Scene for EndingScene { .text(format!("-{}", res.early_kind[id])) .pos(x + 0.03, y) .size(s) - .color(Color::from_hex_rgb(0x81d4fa)) + .color(Color::from_hex(0x81d4fa)) .draw_using(&BOLD_FONT); ui.text(format!("+{}", res.late_kind[id])) .pos(r.right() + 0.01, y) .size(s) - .color(Color::from_hex_rgb(0xffab91)) + .color(Color::from_hex(0xffab91)) .draw_using(&BOLD_FONT) } else { ui.text(res.counts[id].to_string()).pos(x + 0.06, y).size(s).draw_using(&BOLD_FONT) @@ -514,9 +514,9 @@ impl Scene for EndingScene { r.x -= r.w; r.y -= r.h; self.btn_proceed.render_shadow(ui, r, t, |ui, path| { - ui.fill_path(&path, Color::from_hex_rgb(0x3f51b5)); + ui.fill_path(&path, Color::from_hex(0x3f51b5)); let ir = Rect::new(r.x + 0.05, r.center().y, 0., 0.).feather(0.03); - ui.fill_rect(ir, (*self.icon_proceed, ir)); + ui.fill_rect(ir, ((*self.icon_proceed).clone(), ir)); ui.text(tl!("proceed")) .pos((ir.right() + r.right() - 0.01) / 2., r.center().y) .anchor(0.5, 0.5) @@ -527,9 +527,9 @@ impl Scene for EndingScene { r.x -= r.w + 0.02; self.btn_retry.render_shadow(ui, r, t, |ui, path| { - ui.fill_path(&path, Color::from_hex_rgb(0x78909c)); + ui.fill_path(&path, Color::from_hex(0x78909c)); let ir = Rect::new(r.x + 0.05, r.center().y, 0., 0.).feather(0.03); - ui.fill_rect(ir, (*self.icon_retry, ir)); + ui.fill_rect(ir, ((*self.icon_retry).clone(), ir)); ui.text(tl!("retry")) .pos((ir.right() + r.right() - 0.01) / 2., r.center().y) .anchor(0.5, 0.5) @@ -618,17 +618,17 @@ impl Scene for EndingScene { let icon_x = current_x + (para_w - icon_size) / 2. + skew_offset / 2.; let icon_y = ty - icon_size / 2.; let icon_rect = Rect::new(icon_x, icon_y, icon_size, icon_size); - ui.fill_rect(icon_rect, (*self.mod_icons[mod_idx], icon_rect, ScaleType::Fit, semi_black(0.6))); + ui.fill_rect(icon_rect, ((*self.mod_icons[mod_idx]).clone(), icon_rect, ScaleType::Fit, semi_black(0.6))); current_x = para_right + 0.02; } } } clip_sector(ui, ct, sector_start, sector_start + center_angle, |ui| { - ui.fill_rect(sr, (*self.illustration, sr)); + ui.fill_rect(sr, ((*self.illustration).clone(), sr)); }); let sector_start = (p * 1.4 - 0.3).max(0.) * (angle_end - angle_start - center_angle) + angle_start; clip_sector(ui, ct, sector_start, sector_start + center_angle * 0.5, |ui| { - ui.fill_rect(sr, (*self.illustration, sr.feather(0.15))); + ui.fill_rect(sr, ((*self.illustration).clone(), sr.feather(0.15))); }); ui.alpha(pf, |ui| { @@ -668,7 +668,7 @@ impl Scene for EndingScene { } else { (&self.illustration, 0.55) }; - ui.fill_rect(r, (**tex, r)); + ui.fill_rect(r, (Texture2D::clone(tex), r)); ui.fill_rect(r, semi_black(alpha)); } diff --git a/prpr/src/scene/game.rs b/prpr/src/scene/game.rs index 3a679747..8214b217 100644 --- a/prpr/src/scene/game.rs +++ b/prpr/src/scene/game.rs @@ -414,6 +414,7 @@ impl GameScene { } ui.alpha(res.alpha, |ui| { ui.text("MAGIC BUGFIX TEXT").color(Color::new(0., 0., 0., 0.)).draw(); + ui.text("").draw_using(&PGR_FONT); if tm.now() as f32 - self.pause_first_time <= PAUSE_CLICK_INTERVAL { ui.fill_circle(pause_center.x, pause_center.y, 0.05, Color::new(1., 1., 1., 0.5)); } @@ -511,8 +512,6 @@ impl GameScene { }); } } - // magic to make score visible, refer to phira/src/rate.rs#L219 - ui.text("").draw_using(&PGR_FONT); let lf = -1. + margin; let bt = -top - eps * 2.8 + (1. - p) * 0.4; let scale_point = legacy_aui.then(|| { @@ -560,7 +559,7 @@ impl GameScene { let w = 0.05; let no_retry = self.mode == GameMode::NoRetry; draw_texture_ex( - *res.icon_back, + &res.icon_back, -s * 3. - w, -s + o, c, @@ -571,9 +570,9 @@ impl GameScene { ); let r = Rect::new(0., o, 0., 0.).feather(s); let disabled_color = semi_white(res.alpha * 0.4); - ui.fill_rect(r, (*res.icon_retry, r.feather(0.02), ScaleType::Fit, if no_retry { disabled_color } else { c })); + ui.fill_rect(r, ((*res.icon_retry).clone(), r.feather(0.02), ScaleType::Fit, if no_retry { disabled_color } else { c })); draw_texture_ex( - *res.icon_resume, + &res.icon_resume, s + w, -s + o, if self.dead { disabled_color } else { c }, @@ -935,18 +934,6 @@ impl Scene for GameScene { } tm.now() } else { - #[cfg(target_os = "windows")] - { - // wtf bro. why must particles exist on Windows? - let emitter_config = self.res.emitter.emitter.config.clone(); - let emitter_square_config = self.res.emitter.emitter_square.config.clone(); - self.res.emitter.emitter.config.size = 0.0; - self.res.emitter.emitter_square.config.size = 0.0; - self.res.emitter.emitter.emit(vec2(0.0, 0.0), 1); - self.res.emitter.emitter_square.emit(vec2(0.0, 0.0), 1); - self.res.emitter.emitter.config = emitter_config; - self.res.emitter.emitter_square.config = emitter_square_config; - } self.res.alpha = (1. - (1. - time / Self::BEFORE_TIME).powi(3)) as f32; if self.mode == GameMode::Exercise { self.exercise_range.start @@ -1181,48 +1168,71 @@ impl Scene for GameScene { } let res = &mut self.res; - let asp = ui.viewport.2 as f32 / ui.viewport.3 as f32; if res.update_size(ui.viewport) || self.mode == GameMode::View { set_camera(&res.camera); } let msaa = res.config.sample_count > 1; + // camera setup + let vp = res.camera.viewport.unwrap_or(ui.viewport); + let viewport_window = Some(ui.viewport); + let viewport_chart = if res.chart_target.is_some() { + Some((vp.0 - ui.viewport.0, vp.1 - ui.viewport.1, vp.2, vp.3)) + } else { + res.camera.viewport + }; + + let asp2_window = ui.viewport.2 as f32 / ui.viewport.3 as f32; + let asp2_chart = vp.2 as f32 / vp.3 as f32; + let asp2_ui = vp.2 as f32 / vp.3 as f32; + let chart_onto = res .chart_target .as_ref() .map(|it| if msaa { it.input() } else { it.output() }) - .or(res.camera.render_target); - push_camera_state(); + .or(res.camera.render_target.clone()); + + let h = 1. / res.aspect_ratio; set_camera(&Camera2D { - zoom: vec2(1., -asp), - viewport: if res.chart_target.is_some() { None } else { Some(ui.viewport) }, - render_target: chart_onto, + zoom: vec2(1., asp2_window), + viewport: if res.chart_target.is_some() { None } else { viewport_window }, + render_target: chart_onto.clone(), ..Default::default() }); clear_background(BLACK); - draw_background(*res.background); - pop_camera_state(); + draw_background(&res.background); - let chart_target_vp = if res.chart_target.is_some() { - let vp = res.camera.viewport.unwrap(); - Some((vp.0 - ui.viewport.0, vp.1 - ui.viewport.1, vp.2, vp.3)) - } else { - res.camera.viewport - }; - self.gl.quad_gl.render_pass(chart_onto.map(|it| it.render_pass)); - self.gl.quad_gl.viewport(chart_target_vp); + { + let dim_alpha = 0.7; + //let alpha = res.alpha * (1. - dim_alpha) + dim_alpha; + let dim = Color::new(0.1, 0.1, 0.1, dim_alpha * res.alpha); + let x_range = vp.0 as f32 / ui.viewport.2 as f32; + let y_range = vp.1 as f32 / vp.3 as f32; + draw_rectangle(-1., -h,x_range * 2., h * 2., dim); // Left + draw_rectangle(1., -h,-x_range * 2., h * 2., dim); // Right + draw_rectangle(-1., -h,2., -y_range * 2., dim); // Top + draw_rectangle(-1., h,2., y_range * 2., dim); // Bottom + draw_rectangle(x_range * 2. - 1., -h, (1. - x_range * 2.) * 2., h * 2., Color::new(0., 0., 0., res.alpha * res.info.background_dim)); + } - let h = 1. / res.aspect_ratio; - draw_rectangle(-1., -h, 2., h * 2., Color::new(0., 0., 0., res.alpha * res.info.background_dim)); + let chart_zoom = vec2(1., asp2_chart); + let chart_viewport = viewport_chart; + set_camera( &Camera2D { + zoom: chart_zoom, + viewport: chart_viewport, + render_target: chart_onto.clone(), + ..Default::default() + }); + self.gl.quad_gl.render_pass(chart_onto.as_ref().map(|it| it.render_pass.raw_miniquad_id())); self.chart.render(ui, res); self.gl.quad_gl.render_pass( res.chart_target .as_ref() - .map(|it| it.output().render_pass) - .or_else(|| res.camera.render_pass()), + .map(|it| it.output().render_pass.raw_miniquad_id()) + .or_else(|| Some(res.camera.render_pass()?.raw_miniquad_id())), ); self.bad_notes.retain(|dummy| dummy.render(res)); @@ -1231,46 +1241,93 @@ impl Scene for GameScene { if res.config.particle { res.emitter.draw(dt); } - self.ui(ui, tm)?; - self.overlay_ui(ui, tm)?; - if self.mode == GameMode::TweakOffset { - push_camera_state(); - self.gl.quad_gl.viewport(None); + if !res.no_effect { + set_camera(&Camera2D { + zoom: vec2(1., asp2_chart), + render_target: chart_onto.clone(), + viewport: Some(ui.viewport), + ..Default::default() + }); + for effect in &self.chart.extra.effects { + effect.render(res); + } + } + + { set_camera(&Camera2D { - zoom: vec2(1., -screen_aspect()), - render_target: self.res.chart_target.as_ref().map(|it| it.output()).or(self.res.camera.render_target), + zoom: vec2(1., asp2_ui), + viewport: chart_viewport, + render_target: self.res.chart_target.as_ref().map(|it| it.output()).or(self.res.camera.render_target.clone()), ..Default::default() }); - self.tweak_offset(ui, Self::interactive(&self.res, &self.state)); - pop_camera_state(); + self.ui(ui, tm)?; } if !self.res.no_effect && !self.effects.is_empty() { - push_camera_state(); set_camera(&Camera2D { - zoom: vec2(1., asp), + zoom: vec2(1., asp2_window), + render_target: chart_onto.clone(), + viewport: Some(ui.viewport), + ..Default::default() + }); + for effect in &self.effects { + effect.render(&mut self.res); + } + } + + { + set_camera(&Camera2D { + zoom: vec2(1., 1.), + viewport: viewport_window, + render_target: self.res.chart_target.as_ref().map(|it| it.output()).or(self.res.camera.render_target.clone()), + ..Default::default() + }); + if tm.paused() { + draw_rectangle(-1., -1., 2., 2., Color::new(0., 0., 0., 0.6)); + } + } + + { + set_camera(&Camera2D { + zoom: vec2(1., asp2_window), + viewport: viewport_window, + render_target: self.res.chart_target.as_ref().map(|it| it.output()).or(self.res.camera.render_target.clone()), ..Default::default() }); - for e in &self.effects { - e.render(&mut self.res); + if self.mode == GameMode::TweakOffset { + self.tweak_offset(ui, Self::interactive(&self.res, &self.state)); + } + if self.res.config.touch_debug { + for touch in Judge::get_touches() { + ui.fill_circle(touch.position.x, touch.position.y, 0.04, Color { a: 0.4, ..RED }); + } } - pop_camera_state(); } - if msaa || !self.res.no_effect { + + { + set_camera(&Camera2D { + zoom: vec2(1., asp2_chart), + viewport: viewport_chart, + render_target: self.res.chart_target.as_ref().map(|it| it.output()).or(self.res.camera.render_target.clone()), + ..Default::default() + }); + self.overlay_ui(ui, tm)?; + } + + if !self.res.no_effect || msaa { // render the texture onto screen if let Some(target) = &self.res.chart_target { self.gl.flush(); - push_camera_state(); self.gl.quad_gl.viewport(None); set_camera(&Camera2D { - zoom: vec2(1., asp), - render_target: self.res.camera.render_target, + zoom: vec2(1., asp2_window), + render_target: self.res.camera.render_target.clone(), viewport: Some(ui.viewport), ..Default::default() }); draw_texture_ex( - target.output().texture, + &target.output().texture, -1., -ui.top, WHITE, @@ -1279,7 +1336,6 @@ impl Scene for GameScene { ..Default::default() }, ); - pop_camera_state(); } } Ok(()) diff --git a/prpr/src/scene/loading.rs b/prpr/src/scene/loading.rs index c08332b4..a34fd7b6 100644 --- a/prpr/src/scene/loading.rs +++ b/prpr/src/scene/loading.rs @@ -170,12 +170,12 @@ impl Scene for LoadingScene { fn render(&mut self, tm: &mut TimeManager, ui: &mut Ui) -> Result<()> { let mut cam = ui.camera(); - let asp = -cam.zoom.y; + let asp = cam.zoom.y; let top = 1. / asp; let t = tm.now() as f32; - cam.render_target = self.target; + cam.render_target = self.target.clone(); set_camera(&cam); - draw_background(*self.background); + draw_background(&self.background); ui.alpha((t / FADE_IN_TIME).min(1.), |ui| { let dx = if t > self.finish_time { @@ -202,7 +202,7 @@ impl Scene for LoadingScene { rounded_rect_shadow(ui, r, &config); clip_rounded_rect(ui, r, config.radius, |ui| { ui.fill_rect(r, Color { a: 0.6, ..self.theme_color }); - ui.fill_rect(ir, (*self.illustration, ir)); + ui.fill_rect(ir, (Texture2D::clone(&*self.illustration), ir)); ui.fill_rect(ir, (semi_black(0.5), (ir.x, ir.bottom()), Color::default(), (ir.x, ir.y))); }); diff --git a/prpr/src/ui.rs b/prpr/src/ui.rs index 5b959cac..60dabd57 100644 --- a/prpr/src/ui.rs +++ b/prpr/src/ui.rs @@ -22,6 +22,9 @@ pub use shadow::*; mod text; pub use text::{DrawText, TextPainter}; +mod input; +pub use input::InlineInputBox; + pub use glyph_brush::ab_glyph::FontArc; use crate::{ @@ -136,7 +139,7 @@ impl VertexBuilder { pub fn commit(&self) { let gl = unsafe { get_internal_gl() }.quad_gl; - gl.texture(self.shading.texture()); + gl.texture(self.shading.texture().as_ref()); gl.draw_mode(DrawMode::Triangles); gl.geometry(&self.vertices, &self.indices); } @@ -687,7 +690,7 @@ impl<'a> Ui<'a> { pub fn camera(&self) -> Camera2D { Camera2D { - zoom: vec2(1., -self.viewport.2 as f32 / self.viewport.3 as f32), + zoom: vec2(1., self.viewport.2 as f32 / self.viewport.3 as f32), viewport: Some(self.viewport), ..Default::default() } @@ -769,7 +772,7 @@ impl<'a> Ui<'a> { fn emit_lyon(&mut self, texture: Option) { let gl = unsafe { get_internal_gl() }.quad_gl; - gl.texture(texture); + gl.texture(texture.as_ref()); gl.draw_mode(DrawMode::Triangles); gl.geometry(&std::mem::take(&mut self.vertex_buffers.vertices), &std::mem::take(&mut self.vertex_buffers.indices)); } @@ -954,11 +957,11 @@ impl<'a> Ui<'a> { } pub fn accent(&self) -> Color { - Color::from_hex_rgb(0x2196f3) + Color::from_hex(0x2196f3) } pub fn background(&self) -> Color { - Color::from_hex_rgb(0x2a323c) + Color::from_hex(0x2a323c) } pub fn button(&mut self, id: &str, rect: Rect, text: impl Into) -> bool { @@ -1134,7 +1137,7 @@ impl<'a> Ui<'a> { let rect = Rect::new(cx - r, cy - r, r * 2., r * 2.); match avatar { Ok(Some(avatar)) => { - self.fill_circle(cx, cy, r, (*avatar, rect)); + self.fill_circle(cx, cy, r, (Texture2D::clone(&*avatar), rect)); } Ok(None) => { self.loading( @@ -1151,7 +1154,7 @@ impl<'a> Ui<'a> { } Err(icon) => { self.fill_circle(cx, cy, r, semi_black(0.2)); - self.fill_circle(cx, cy, r, (*icon, rect.feather(-0.025), ScaleType::CropCenter, WHITE)); + self.fill_circle(cx, cy, r, (Texture2D::clone(&*icon), rect.feather(-0.025), ScaleType::CropCenter, WHITE)); } } self.stroke_circle(cx, cy, r, 0.004, WHITE); diff --git a/prpr/src/ui/billboard.rs b/prpr/src/ui/billboard.rs index a54b8bdf..d6333c0d 100644 --- a/prpr/src/ui/billboard.rs +++ b/prpr/src/ui/billboard.rs @@ -157,7 +157,7 @@ impl BillBoard { tx.ui.fill_rect(r, Color::new(1., 1., 1., 0.4)); if let Some(icons) = self.icons.as_ref() { let r = r.feather(-0.02); - tx.ui.fill_rect(r, (*icons[msg.kind.clone() as u8 as usize], r, ScaleType::Fit)); + tx.ui.fill_rect(r, (Texture2D::clone(&icons[msg.kind.clone() as u8 as usize]), r, ScaleType::Fit)); } tx.draw(); true diff --git a/prpr/src/ui/dialog.rs b/prpr/src/ui/dialog.rs index 47c988fd..02cdd256 100644 --- a/prpr/src/ui/dialog.rs +++ b/prpr/src/ui/dialog.rs @@ -4,6 +4,7 @@ use super::{DRectButton, RectButton, Scroll, Ui}; use crate::{core::BOLD_FONT, ext::RectExt, scene::show_message}; use anyhow::Error; use macroquad::prelude::*; +use macroquad::miniquad; const WIDTH_RADIO: f32 = 0.5; const HEIGHT_RATIO: f32 = 0.7; @@ -71,7 +72,7 @@ impl Dialog { buttons: vec![tl!("error-copy").to_string(), tl!("ok").to_string()], listener: Some(Box::new(move |_dialog, pos| { if pos == 0 { - unsafe { get_internal_gl() }.quad_context.clipboard_set(&error); + miniquad::window::clipboard_set(&error); show_message(tl!("error-copied")).ok(); } false diff --git a/prpr/src/ui/input.rs b/prpr/src/ui/input.rs new file mode 100644 index 00000000..e2ea9ca0 --- /dev/null +++ b/prpr/src/ui/input.rs @@ -0,0 +1,927 @@ +prpr_l10n::tl_file!("input"); + +use super::Ui; +use crate::{ + ext::RectExt, judge::take_wheel, ui::scroll::WHEEL_STEP, +}; +use macroquad::{ + input::Touch, + prelude::*, + miniquad::window::{clipboard_get, clipboard_set}, +}; + +const CONTEXT_MENU_MENU_W: f32 = 0.12; +const CONTEXT_MENU_ITEM_Y: f32 = 0.04; + +struct ContextMenu { + visible: bool, + position: (f32, f32), + rect: Rect, + items: Vec<(Rect, String)>, +} + +impl Default for ContextMenu { + fn default() -> Self { + Self { + visible: false, + position: (0.0, 0.0), + rect: Rect::default(), + items: vec![ + (Rect::default(), tl!("select-all").to_string()), + (Rect::default(), tl!("copy").to_string()), + (Rect::default(), tl!("cut").to_string()), + (Rect::default(), tl!("paste").to_string()), + ], + } + } +} + +pub struct InlineInputBox { + buffer: String, + rect: Rect, + multiline: bool, + password: bool, + + state: State, + context_menu: ContextMenu, +} + +#[derive(Default)] +enum TouchMode { + #[default] + None, + Selecting, + Scrolling, + Wating, +} + +#[derive(Default)] +struct State { + active: bool, + cursor: usize, + selection_anchor: Option, + backspace_time: Option, + delete_time: Option, + last_pop_time: Option, + + left_arrow_time: Option, + right_arrow_time: Option, + up_arrow_time: Option, + down_arrow_time: Option, + last_cursor_time: Option, + + cursor_positions: Vec<(f32, f32)>, + scroll_x: f32, + scroll_y: f32, + + touch_start_pos: (f32, f32), + touch_start_time: f64, + touch_is_moved: bool, + touch_mode: TouchMode, + touch_start_scroll_x: f32, + touch_start_scroll_y: f32, + touch_scale_x: f32, + touch_scale_y: f32, + manual_scroll: bool, +} + +impl InlineInputBox { + pub fn new() -> Self { + Self { + buffer: String::new(), + rect: Rect::new(0., 0., 0., 0.), + multiline: false, + password: false, + state: State::default(), + context_menu: ContextMenu::default(), + } + } + + pub fn activate(&mut self, initial: &str, multiline: bool, password: bool) { + self.state.active = true; + self.buffer = initial.to_string(); + self.multiline = multiline; + self.password = password; + self.state.cursor = initial.chars().count(); + self.state.selection_anchor = None; + self.state.backspace_time = None; + self.state.scroll_x = 0.0; + self.state.scroll_y = 0.0; + self.state.manual_scroll = false; + miniquad::window::set_ime_enabled(true); + miniquad::window::show_keyboard(true); + + while get_char_pressed().is_some() {} + } + + pub fn is_active(&self) -> bool { + self.state.active + } + + pub fn cancel(&mut self) { + self.state.active = false; + self.buffer.clear(); + self.state.selection_anchor = None; + self.state.backspace_time = None; + self.context_menu.visible = false; + miniquad::window::set_ime_enabled(false); + miniquad::window::show_keyboard(false); + } + + pub fn confirm(&mut self) -> String { + self.state.active = false; + self.state.selection_anchor = None; + self.state.backspace_time = None; + self.context_menu.visible = false; + miniquad::window::set_ime_enabled(false); + miniquad::window::show_keyboard(false); + std::mem::take(&mut self.buffer) + } + + fn byte_at(&self, char_idx: usize) -> usize { + self.buffer.char_indices().nth(char_idx).map(|(i, _)| i).unwrap_or(self.buffer.len()) + } + + fn remove_char_at(&mut self, idx: usize) { + let start = self.byte_at(idx); + let end = self.byte_at(idx + 1); + self.buffer.replace_range(start..end, ""); + } + + fn text_before(&self) -> &str { + let end = self.byte_at(self.state.cursor); + &self.buffer[..end] + } + + fn selection_range(&self) -> Option<(usize, usize)> { + self.state.selection_anchor.map(|anchor| { + let start = anchor.min(self.state.cursor); + let end = anchor.max(self.state.cursor); + (start, end) + }) + } + + fn selected_text(&self) -> Option { + self.selection_range().map(|(start, end)| { + let start_byte = self.byte_at(start); + let end_byte = self.byte_at(end); + self.buffer[start_byte..end_byte].to_string() + }) + } + + fn delete_selection(&mut self) -> bool { + if let Some((start, end)) = self.selection_range() { + let start_byte = self.byte_at(start); + let end_byte = self.byte_at(end); + self.buffer.replace_range(start_byte..end_byte, ""); + self.state.cursor = start; + self.state.selection_anchor = None; + true + } else { + false + } + } + + fn update_ime(&self, ui: &Ui, cursor_screen: (f32, f32)) { + let dpi = miniquad::window::dpi_scale(); + let (x, y) = ui.to_global(cursor_screen); + let vp = ui.viewport; + let asp = vp.2 as f32 / vp.3 as f32; + let x = (x + 1.0) * 0.5 * vp.2 as f32 * dpi; + let y = (y * asp + 1.0) * 0.5 * vp.3 as f32 * dpi; + miniquad::window::set_ime_position(x as i32, y as i32); + } + + pub fn touch(&mut self, touch: &Touch) -> bool { + let p = touch.position; + let in_rect = self.rect.contains(p); + let cursor = self.find_nearest_cursor(p.x, p.y); + match touch.phase { + TouchPhase::Started => {} + TouchPhase::Moved | TouchPhase::Stationary => {} + TouchPhase::Ended | TouchPhase::Cancelled => { + if self.context_menu.visible && !matches!(self.state.touch_mode, TouchMode::Wating) { + if self.context_menu.rect.contains(p) { + for (i, btn_rect) in self.context_menu.items.iter().enumerate() { + if btn_rect.0.contains(p) { + match i { + 0 => { // Select All + self.state.selection_anchor = Some(0); + self.state.cursor = self.buffer.chars().count(); + } + 1 => { // Copy + if let Some(text) = self.selected_text() { + clipboard_set(&text); + } + } + 2 => { // Cut + if let Some(text) = self.selected_text() { + clipboard_set(&text); + self.delete_selection(); + } + } + 3 => { // Paste + if let Some(text) = clipboard_get().map(|s| s.to_string()) { + self.delete_selection(); + let byte_pos = self.byte_at(self.state.cursor); + self.buffer.insert_str(byte_pos, &text); + self.state.cursor += text.chars().count(); + } + } + _ => {} + } + self.context_menu.visible = false; + break; + } + } + } else { + self.context_menu.visible = false; + } + } + } + } + if is_mouse_button_down(MouseButton::Left) || is_mouse_button_released(MouseButton::Left) { + match touch.phase { + TouchPhase::Moved | TouchPhase::Stationary => { + if !self.context_menu.visible { + self.state.cursor = cursor; + } + false + } + TouchPhase::Ended | TouchPhase::Cancelled => { + false + } + TouchPhase::Started => { + if !self.context_menu.visible && in_rect { + self.state.cursor = cursor; + self.state.selection_anchor = Some(cursor); + self.state.manual_scroll = false; + } + !in_rect + } + } + } else if is_mouse_button_down(MouseButton::Right) || is_mouse_button_released(MouseButton::Right) { + match touch.phase { + TouchPhase::Moved | TouchPhase::Stationary => { + false + } + TouchPhase::Ended | TouchPhase::Cancelled => { + if in_rect { + if !self.password && self.rect.contains(p) && !self.context_menu.visible { + self.context_menu.visible = true; + self.context_menu.position = ( + p.x.max(self.rect.x).min(self.rect.right() - CONTEXT_MENU_MENU_W), + p.y.max(self.rect.y).min(self.rect.bottom() - CONTEXT_MENU_ITEM_Y * self.context_menu.items.len() as f32) + ); + } + } + false + } + TouchPhase::Started => { + !in_rect + } + } + } else { + match touch.phase { + TouchPhase::Started => { + if in_rect { + self.state.touch_start_pos = (p.x, p.y); + self.state.touch_start_time = get_time(); + self.state.touch_mode = TouchMode::None; + self.state.touch_start_scroll_x = self.state.scroll_x; + self.state.touch_start_scroll_y = self.state.scroll_y; + self.state.touch_is_moved = false; + } + !in_rect + } + TouchPhase::Moved | TouchPhase::Stationary => { + let dx = p.x - self.state.touch_start_pos.0; + let dy = p.y - self.state.touch_start_pos.1; + let dist = (dx * dx + dy * dy).sqrt(); + if !self.state.touch_is_moved && dist > 0.05 { + self.state.touch_is_moved = true; + } + let dt = get_time() - self.state.touch_start_time; + + match self.state.touch_mode { + TouchMode::None => { + if self.state.touch_is_moved { + self.state.touch_mode = TouchMode::Scrolling; + self.state.manual_scroll = true; + } else if dt > 0.5 && self.state.selection_anchor.is_none() { + self.state.touch_mode = TouchMode::Selecting; + self.state.cursor = cursor; + self.state.selection_anchor = Some(cursor); + self.state.manual_scroll = false; + } + } + TouchMode::Scrolling => { + let dx_ui = dx * self.state.touch_scale_x; + let dy_ui = dy * self.state.touch_scale_y; + self.state.scroll_x = self.state.touch_start_scroll_x - dx_ui; + self.state.scroll_y = self.state.touch_start_scroll_y - dy_ui; + } + TouchMode::Selecting => { + self.state.cursor = cursor; + } + _ => {} + } + false + } + TouchPhase::Ended | TouchPhase::Cancelled => { + let dt = get_time() - self.state.touch_start_time; + if dt > 0.5 && !self.state.touch_is_moved && !self.password && self.rect.contains(p) && !self.context_menu.visible { + self.context_menu.visible = true; + self.context_menu.position = ( + self.state.cursor_positions.get(self.state.cursor).map_or(p.x, |&(x, _)| x) + .max(self.rect.x).min(self.rect.right() - CONTEXT_MENU_MENU_W), + self.state.cursor_positions.get(self.state.cursor).map_or(p.y, |&(_, y)| y) + .max(self.rect.y).min(self.rect.bottom() - CONTEXT_MENU_ITEM_Y * self.context_menu.items.len() as f32), + ); + self.state.touch_mode = TouchMode::Wating; + } + if matches!(self.state.touch_mode, TouchMode::None) && in_rect && !self.context_menu.visible { + self.state.cursor = cursor; + self.state.selection_anchor = None; + self.state.manual_scroll = false; + miniquad::window::show_keyboard(true); + } + self.state.touch_mode = TouchMode::None; + false + } + } + } + } + + fn find_nearest_cursor(&self, touch_x: f32, touch_y: f32) -> usize { + if self.state.cursor_positions.is_empty() { + return 0; + } + let mut best_idx = 0; + let mut best_dist = f32::MAX; + for (i, &(px, py)) in self.state.cursor_positions.iter().enumerate() { + let dx = touch_x - px; + let dy = touch_y - py; + let dist = dx * dx + dy * dy * 10000.0; + if dist < best_dist { + best_dist = dist; + best_idx = i; + } + } + best_idx + } + + fn find_nearest_col_cursor(&self, from_cursor: usize, to_cursor: usize) -> usize { + if self.state.cursor_positions.is_empty() { + return 0; + } + let mut best_idx = 0; + let mut best_dist = f32::MAX; + let x = self.state.cursor_positions.get(from_cursor).map_or(0.0, |&(x, _)| x); + let y = self.state.cursor_positions.get(to_cursor).map_or(0.0, |&(_, y)| y); + for (i, &(px, py)) in self.state.cursor_positions.iter().enumerate() { + let dx = x - px; + let dy = y - py; + let dist = dx * dx + dy * dy * 10000.0; + if dist < best_dist { + best_dist = dist; + best_idx = i; + } + } + best_idx + } + + fn cursor_right(&mut self, shift: bool) { + if shift { + if self.state.selection_anchor.is_none() { + self.state.selection_anchor = Some(self.state.cursor); + } + } else { + self.state.selection_anchor = None; + } + if self.state.cursor < self.buffer.chars().count() { + self.state.cursor += 1; + } + } + + fn cursor_left(&mut self, shift: bool) { + if shift { + if self.state.selection_anchor.is_none() { + self.state.selection_anchor = Some(self.state.cursor); + } + } else { + self.state.selection_anchor = None; + } + if self.state.cursor > 0 { + self.state.cursor -= 1; + } + } + + fn cursor_up(&mut self, shift: bool) { + if shift { + if self.state.selection_anchor.is_none() { + self.state.selection_anchor = Some(self.state.cursor); + } + } else { + self.state.selection_anchor = None; + } + let before = self.text_before(); + if let Some(line_start) = before.rfind('\n') { + let col = before.len() - line_start - 1; + let prev_line = &before[..line_start]; + let prev_start = prev_line.rfind('\n').map(|i| i + 1).unwrap_or(0); + let prev_col = col.min(line_start - prev_start); + let target_byte = prev_start + prev_col; + let target_byte_adj = self.find_nearest_col_cursor(self.state.cursor, target_byte); + self.state.cursor = self.buffer.char_indices().take_while(|(i, _)| *i < target_byte_adj).count(); + } + } + + fn cursor_down(&mut self, shift: bool) { + if shift { + if self.state.selection_anchor.is_none() { + self.state.selection_anchor = Some(self.state.cursor); + } + } else { + self.state.selection_anchor = None; + } + let before = self.text_before(); + let before_byte = self.byte_at(self.state.cursor); + let line_start_byte = before.rfind('\n').map(|i| i + 1).unwrap_or(0); + let col = before_byte - line_start_byte; + let after = &self.buffer[before_byte..]; + if let Some(rel_nl) = after.find('\n') { + let next_line_start = before_byte + rel_nl + 1; + let next_line_end = self.buffer[next_line_start..].find('\n').map(|i| next_line_start + i).unwrap_or(self.buffer.chars().count()); + let next_line_len = next_line_end - next_line_start; + let target_col = col.min(next_line_len); + let target_byte = next_line_start + target_col; + let target_byte_adj = self.find_nearest_col_cursor(self.state.cursor, target_byte); + self.state.cursor = self.buffer.char_indices().take_while(|(i, _)| *i < target_byte_adj).count(); + } + } + + pub fn update(&mut self) { + let now = get_time(); + let ctrl = is_key_down(KeyCode::LeftControl) || is_key_down(KeyCode::RightControl); + let shift = is_key_down(KeyCode::LeftShift) || is_key_down(KeyCode::RightShift); + + // Arrow keys + if is_key_pressed(KeyCode::Right) { + self.state.right_arrow_time = Some(now); + self.cursor_right(shift); + } else if let Some(arrow_time) = self.state.right_arrow_time { + if is_key_down(KeyCode::Right) { + if now - arrow_time > 0.5 { + if self.state.last_cursor_time.map_or(true, |t| now - t > 0.02) { + self.state.last_cursor_time = Some(now); + self.cursor_right(shift); + } + } + } else { + self.state.right_arrow_time = None; + } + } + if is_key_pressed(KeyCode::Left) { + self.state.left_arrow_time = Some(now); + self.cursor_left(shift); + } else if let Some(arrow_time) = self.state.left_arrow_time { + if is_key_down(KeyCode::Left) { + if now - arrow_time > 0.5 { + if self.state.last_cursor_time.map_or(true, |t| now - t > 0.02) { + self.state.last_cursor_time = Some(now); + self.cursor_left(shift); + } + } + } else { + self.state.left_arrow_time = None; + } + } + if self.multiline { + if is_key_pressed(KeyCode::Up) { + self.state.up_arrow_time = Some(now); + self.cursor_up(shift); + } else if let Some(arrow_time) = self.state.up_arrow_time { + if is_key_down(KeyCode::Up) { + if now - arrow_time > 0.5 { + if self.state.last_cursor_time.map_or(true, |t| now - t > 0.02) { + self.state.last_cursor_time = Some(now); + self.cursor_up(shift); + } + } + } else { + self.state.up_arrow_time = None; + } + } + if is_key_pressed(KeyCode::Down) { + self.state.down_arrow_time = Some(now); + self.cursor_down(shift); + } else if let Some(arrow_time) = self.state.down_arrow_time { + if is_key_down(KeyCode::Down) { + if now - arrow_time > 0.5 { + if self.state.last_cursor_time.map_or(true, |t| now - t > 0.02) { + self.state.last_cursor_time = Some(now); + self.cursor_down(shift); + } + } + } else { + self.state.down_arrow_time = None; + } + } + } + if is_key_pressed(KeyCode::Home) { + if shift { + if self.state.selection_anchor.is_none() { + self.state.selection_anchor = Some(self.state.cursor); + } + } else { + self.state.selection_anchor = None; + } + let before = self.text_before(); + self.state.cursor = before.rfind('\n').map(|i| self.buffer[..i].chars().count() + 1).unwrap_or(0); + } + if is_key_pressed(KeyCode::End) { + if shift { + if self.state.selection_anchor.is_none() { + self.state.selection_anchor = Some(self.state.cursor); + } + } else { + self.state.selection_anchor = None; + } + let after_byte = self.byte_at(self.state.cursor); + self.state.cursor = self.buffer[after_byte..].find('\n').map(|i| { + self.buffer[..after_byte + i].chars().count() + }).unwrap_or(self.buffer.chars().count()); + } + + // Copy/Paste/Cut + if ctrl & !self.password { + if is_key_pressed(KeyCode::C) { + if let Some(text) = self.selected_text() { + clipboard_set(&text); + } + } + if is_key_pressed(KeyCode::X) { + if let Some(text) = self.selected_text() { + clipboard_set(&text); + self.delete_selection(); + } + } + if is_key_pressed(KeyCode::V) { + if let Some(text) = clipboard_get().map(|s| s.to_string()) { + // Delete selection first + self.delete_selection(); + let byte_pos = self.byte_at(self.state.cursor); + self.buffer.insert_str(byte_pos, &text); + self.state.cursor += text.chars().count(); + } + } + if is_key_pressed(KeyCode::A) { + self.state.selection_anchor = Some(0); + self.state.cursor = self.buffer.chars().count(); + } + } + + if is_key_pressed(KeyCode::Backspace) { + self.state.backspace_time = Some(now); + if !self.delete_selection() { + if self.state.cursor > 0 { + self.state.cursor -= 1; + self.remove_char_at(self.state.cursor); + } + } + } else if let Some(backspace_time) = self.state.backspace_time { + if is_key_down(KeyCode::Backspace) { + if now - backspace_time > 0.5 { + if self.state.last_pop_time.map_or(true, |t| now - t > 0.02) { + self.state.last_pop_time = Some(now); + if !self.delete_selection() { + if self.state.cursor > 0 { + self.state.cursor -= 1; + self.remove_char_at(self.state.cursor); + } + } + } + } + } else { + self.state.backspace_time = None; + } + } + + // Delete key + if is_key_pressed(KeyCode::Delete) { + self.state.delete_time = Some(now); + if !self.delete_selection() { + if self.state.cursor < self.buffer.chars().count() { + self.remove_char_at(self.state.cursor); + } + } + } else if let Some(delete_time) = self.state.delete_time { + if is_key_down(KeyCode::Delete) { + if now - delete_time > 0.5 { + if self.state.last_pop_time.map_or(true, |t| now - t > 0.02) { + self.state.last_pop_time = Some(now); + if !self.delete_selection() { + if self.state.cursor < self.buffer.chars().count() { + self.remove_char_at(self.state.cursor); + } + } + } + } + } else { + self.state.delete_time = None; + } + } + + // Enter key + if is_key_pressed(KeyCode::Enter) { + if self.multiline { + self.delete_selection(); + let byte_pos = self.byte_at(self.state.cursor); + self.buffer.insert(byte_pos, '\n'); + self.state.cursor += 1; + } + } + + // Character input + while let Some(ch) = get_char_pressed() { + if !ch.is_control() { + // Delete selection first if any + self.delete_selection(); + let byte_pos = self.byte_at(self.state.cursor); + self.buffer.insert(byte_pos, ch); + self.state.cursor += 1; + } + } + + if is_key_pressed(KeyCode::Escape) { + self.cancel(); + } + + // Mouse wheel + if self.multiline { + let (x, y) = take_wheel(); + if x.abs() > 1e-5 || y.abs() > 1e-5 { + self.state.scroll_x -= x * WHEEL_STEP; + self.state.scroll_y -= y * WHEEL_STEP; + self.state.manual_scroll = true; + } + } + } + + pub fn render(&mut self, ui: &mut Ui, rect: Rect, t: f32, placeholder: &str) { + self.rect = ui.rect_to_global(rect); + self.state.touch_scale_x = if self.rect.w > 0.0 { rect.w / self.rect.w } else { 1.0 }; + self.state.touch_scale_y = if self.rect.h > 0.0 { rect.h / self.rect.h } else { 1.0 }; + let bx = rect.x; + let by = rect.y; + let bw = rect.w; + let bh = rect.h; + + ui.fill_path( + &Rect::new(bx, by, bw, bh).rounded(0.008), + Color::new(0.35, 0.5, 1.0, t), + ); + ui.fill_path( + &Rect::new(bx + 0.002, by + 0.002, bw - 0.004, bh - 0.004).rounded(0.006), + Color::new(0.15, 0.15, 0.18, t), + ); + + let line_h = ui.text("0").size(0.42).measure().h; + let text_x = bx + 0.02; + let max_w = bw - 0.04; + let max_h = bh - 0.04; + let clip = Rect::new(bx + 0.002, by + 0.002, bw - 0.004, bh - 0.004); + ui.scissor(clip, |ui| { + if self.multiline { + let text_y = by + 0.02; + let line_h_with_space = ui.text("0\n0").size(0.42).multiline().measure().h - line_h; + if self.buffer.is_empty() { + ui.text(placeholder) + .pos(text_x, text_y) + .anchor(0.0, 0.0) + .no_baseline() + .size(0.42) + .color(Color::new(1.0, 1.0, 1.0, t * 0.3)) + .draw(); + ui.fill_rect(Rect::new(text_x, text_y, 0.003, line_h + 0.01), Color::new(1.0, 1.0, 1.0, t * 0.9)); + self.update_ime(ui, (text_x, text_y)); + self.state.cursor_positions.clear(); + self.state.cursor_positions.push(ui.to_global((text_x, text_y))); + return; + } + let display = if self.password { + &self.buffer.chars().map(|_| '•').collect::() + } else { + &self.buffer + }; + let display_before = if self.password { + &self.text_before().chars().map(|_| '•').collect::() + } else { + self.text_before() + }; + let line_start = display_before.rfind('\n').map(|i| i + 1).unwrap_or(0); + let cursor_line_text = &display_before[line_start..]; + let line_num = display_before.chars().filter(|c| *c == '\n').count() as f32; + let cursor_w = ui.text(cursor_line_text).size(0.42).multiline().measure().w; + let cursor_y = line_num * line_h_with_space; + let full_text = ui.text(display).size(0.42).multiline().measure(); + let text_x_adj = if full_text.w > max_w { + let margin = max_w * 0.1; + let lo = (cursor_w - max_w + margin).max(0.0); + let hi = (cursor_w - margin).max(0.0).min(full_text.w - max_w); + if lo <= hi { + self.state.scroll_x = self.state.scroll_x.clamp(lo, hi); + } else { + self.state.scroll_x = hi; + } + text_x - self.state.scroll_x + } else { + self.state.scroll_x = 0.0; + text_x + }; + let text_y_adj = if full_text.h > max_h { + if self.state.manual_scroll { + let max_scroll = (full_text.h - max_h).max(0.0); + self.state.scroll_y = self.state.scroll_y.clamp(0.0, max_scroll); + } else { + let margin = max_h * 0.1; + let lo = (cursor_y + line_h - max_h + margin).max(0.0); + let hi = (cursor_y - margin).max(0.0).min(full_text.h - max_h); + if lo <= hi { + self.state.scroll_y = self.state.scroll_y.clamp(lo, hi); + } else { + self.state.scroll_y = hi; + } + } + text_y - self.state.scroll_y + } else { + self.state.scroll_y = 0.0; + text_y + }; + let cursor_y_adj = text_y_adj + line_num * line_h_with_space; + if let Some((sel_start, sel_end)) = self.selection_range() { + let mut char_offset = 0usize; + for (line_idx, line) in display.split('\n').enumerate() { + let line_len = line.chars().count(); + let line_char_start = char_offset; + let line_char_end = char_offset + line_len; + + let overlap_start = sel_start.max(line_char_start).min(line_char_end); + let overlap_end = sel_end.max(line_char_start).min(line_char_end); + + if overlap_start < overlap_end { + let sel_start_in_line = overlap_start - line_char_start; + let sel_end_in_line = overlap_end - line_char_start; + + let start_byte = line.char_indices().nth(sel_start_in_line).map(|(i, _)| i).unwrap_or(line.len()); + let end_byte = line.char_indices().nth(sel_end_in_line).map(|(i, _)| i).unwrap_or(line.len()); + + let start_w = if start_byte == 0 { 0.0 } else { ui.text(&line[..start_byte]).size(0.42).multiline().measure().w }; + let end_w = if end_byte == 0 { 0.0 } else { ui.text(&line[..end_byte]).size(0.42).multiline().measure().w }; + + let y = text_y_adj + line_idx as f32 * line_h_with_space; + let x = text_x_adj + start_w; + let w = end_w - start_w; + if w > 0.0 { + ui.fill_rect(Rect::new(x, y, w, line_h + 0.01), Color::new(0.3, 0.5, 1.0, t * 0.3)); + } + } + + char_offset += line_len + 1; + } + } + ui.text(display) + .pos(text_x_adj, text_y_adj) + .size(0.42) + .color(Color::new(1.0, 1.0, 1.0, t)) + .multiline() + .draw(); + let cx = text_x_adj + cursor_w; + ui.fill_rect(Rect::new(cx, cursor_y_adj, 0.003, line_h + 0.01), Color::new(1.0, 1.0, 1.0, t * 0.9)); + self.update_ime(ui, (cx, cursor_y_adj + 0.002)); + self.state.cursor_positions.clear(); + let chars_count = display.chars().count(); + let mut line_num_cur = 0usize; + let mut line_start_byte = 0usize; + for i in 0..=chars_count { + if i > 0 { + let prev_byte = display.char_indices().nth(i - 1).map(|(j, _)| j).unwrap_or(display.len()); + if display.as_bytes()[prev_byte] == b'\n' { + line_num_cur += 1; + line_start_byte = prev_byte + 1; + } + } + let byte_pos = display.char_indices().nth(i).map(|(j, _)| j).unwrap_or(display.len()); + let line_text = &display[line_start_byte..byte_pos]; + let w = if line_text.is_empty() { + 0.0 + } else { + ui.text(line_text).size(0.42).multiline().measure().w + }; + let x = text_x_adj + w; + let y = text_y_adj + line_num_cur as f32 * line_h_with_space; + self.state.cursor_positions.push(ui.to_global((x, y))); + } + } else { + if self.buffer.is_empty() { + let text_y = by + bh * 0.5; + ui.text(placeholder) + .pos(text_x, text_y) + .anchor(0.0, 0.5) + .no_baseline() + .size(0.42) + .color(Color::new(1.0, 1.0, 1.0, t * 0.3)) + .draw(); + let cursor_x = text_x; + let cursor_y = by + 0.01; + ui.fill_rect(Rect::new(cursor_x, cursor_y, 0.003, bh - 0.02), Color::new(1.0, 1.0, 1.0, t * 0.9)); + self.update_ime(ui, (cursor_x, text_y - line_h * 0.5)); + self.state.cursor_positions.clear(); + self.state.cursor_positions.push(ui.to_global((cursor_x, text_y))); + return; + } + let text_y = by + bh * 0.5; + let display = if self.password { + &self.buffer.chars().map(|_| '•').collect::() + } else { + &self.buffer + }; + let display_before = if self.password { + &self.text_before().chars().map(|_| '•').collect::() + } else { + self.text_before() + }; + let cursor_w = ui.text(display_before).size(0.42).measure().w; + let full_w = ui.text(display).size(0.42).measure().w; + let text_x_adj = if full_w > max_w { + let margin = max_w * 0.1; + let lo = (cursor_w - max_w + margin).max(0.0); + let hi = (cursor_w - margin).max(0.0).min(full_w - max_w); + if lo <= hi { + self.state.scroll_x = self.state.scroll_x.clamp(lo, hi); + } else { + self.state.scroll_x = hi; + } + text_x - self.state.scroll_x + } else { + self.state.scroll_x = 0.0; + text_x + }; + // Draw selection highlight + if let Some((sel_start, sel_end)) = self.selection_range() { + let start_before = &display[..display.char_indices().nth(sel_start).map(|(i, _)| i).unwrap_or(display.len())]; + let end_before = &display[..display.char_indices().nth(sel_end).map(|(i, _)| i).unwrap_or(display.len())]; + let sel_start_w = ui.text(start_before).size(0.42).measure().w; + let sel_end_w = ui.text(end_before).size(0.42).measure().w; + let sel_x = text_x_adj + sel_start_w; + let sel_w = sel_end_w - sel_start_w; + ui.fill_rect(Rect::new(sel_x, by + 0.01, sel_w, bh - 0.02), Color::new(0.3, 0.5, 1.0, t * 0.3)); + } + ui.text(display) + .pos(text_x_adj, text_y) + .anchor(0.0, 0.5) + .no_baseline() + .size(0.42) + .color(Color::new(1.0, 1.0, 1.0, t)) + .draw(); + let cx = text_x_adj + cursor_w; + ui.fill_rect(Rect::new(cx, by + 0.01, 0.003, bh - 0.02), Color::new(1.0, 1.0, 1.0, t * 0.9)); + self.update_ime(ui, (cx, text_y - line_h * 0.5)); + self.state.cursor_positions.clear(); + let chars_count = display.chars().count(); + for i in 0..=chars_count { + let before_i = &display[..display.char_indices().nth(i).map(|(j, _)| j).unwrap_or(display.len())]; + let w = ui.text(before_i).size(0.42).measure().w; + let x = text_x_adj + w; + self.state.cursor_positions.push(ui.to_global((x, text_y))); + } + } + }); + + if self.context_menu.visible && !self.password { + let menu_h = CONTEXT_MENU_ITEM_Y * self.context_menu.items.len() as f32; + let local_pos = ui.to_local(self.context_menu.position); + let menu_x = local_pos.0; + let menu_y = local_pos.1; + + let menu_rect = Rect::new(menu_x, menu_y, CONTEXT_MENU_MENU_W, menu_h); + self.context_menu.rect = ui.rect_to_global(menu_rect); + + ui.fill_path(&menu_rect.rounded(0.006), Color::new(0.2, 0.2, 0.22, 1.0)); + + for (i, item) in self.context_menu.items.iter_mut().enumerate() { + let btn_rect = Rect::new(menu_x + 0.005, menu_y + i as f32 * CONTEXT_MENU_ITEM_Y + 0.005, CONTEXT_MENU_MENU_W - 0.01, CONTEXT_MENU_ITEM_Y - 0.01); + item.0 = ui.rect_to_global(btn_rect); + + ui.text(&item.1) + .pos(menu_x + 0.02, menu_y + i as f32 * CONTEXT_MENU_ITEM_Y + CONTEXT_MENU_ITEM_Y * 0.5) + .anchor(0.0, 0.5) + .no_baseline() + .size(0.38) + .color(Color::new(1.0, 1.0, 1.0, t)) + .draw(); + } + } + } +} diff --git a/prpr/src/ui/scroll.rs b/prpr/src/ui/scroll.rs index bd7a6190..f9d33dff 100644 --- a/prpr/src/ui/scroll.rs +++ b/prpr/src/ui/scroll.rs @@ -12,7 +12,7 @@ use nalgebra::Translation2; use std::collections::VecDeque; const THRESHOLD: f32 = 0.03; -const WHEEL_STEP: f32 = 0.1; +pub const WHEEL_STEP: f32 = 0.1; pub struct VelocityTracker { movements: VecDeque<(f32, Point)>, diff --git a/prpr/src/ui/shading.rs b/prpr/src/ui/shading.rs index 3de6686f..461587fb 100644 --- a/prpr/src/ui/shading.rs +++ b/prpr/src/ui/shading.rs @@ -60,7 +60,7 @@ impl Shading for TextureShading { } fn texture(&self) -> Option { - Some(self.texture.0) + Some(self.texture.0.clone()) } } diff --git a/prpr/src/ui/shadow.rs b/prpr/src/ui/shadow.rs index a88c7ccf..ad5c28b7 100644 --- a/prpr/src/ui/shadow.rs +++ b/prpr/src/ui/shadow.rs @@ -3,7 +3,7 @@ use macroquad::prelude::*; use miniquad::{BlendFactor, BlendState, BlendValue, Equation}; use once_cell::sync::Lazy; -fn alpha_blend_material_params(uniforms: Vec<(String, UniformType)>) -> MaterialParams { +fn alpha_blend_material_params(uniforms: Vec) -> MaterialParams { MaterialParams { pipeline_params: PipelineParams { color_blend: Some(BlendState::new( @@ -19,25 +19,23 @@ fn alpha_blend_material_params(uniforms: Vec<(String, UniformType)>) -> Material } static SHADOW_MATERIAL: Lazy = - Lazy::new(|| load_material(shader::VERTEX, shader::SHADOW_FRAGMENT, alpha_blend_material_params(ShadowConfig::uniforms())).unwrap()); + Lazy::new(|| load_material(ShaderSource::Glsl { vertex: shader::VERTEX, fragment: shader::SHADOW_FRAGMENT }, alpha_blend_material_params(ShadowConfig::uniforms())).unwrap()); static RR_MATERIAL: Lazy = Lazy::new(|| { load_material( - shader::VERTEX, - shader::RR_FRAGMENT, - alpha_blend_material_params(vec![("rect".to_owned(), UniformType::Float4), ("radius".to_owned(), UniformType::Float1)]), + ShaderSource::Glsl { vertex: shader::VERTEX, fragment: shader::RR_FRAGMENT }, + alpha_blend_material_params(vec![UniformDesc::new("rect", UniformType::Float4), UniformDesc::new("radius", UniformType::Float1)]), ) .unwrap() }); static SECTOR_MATERIAL: Lazy = Lazy::new(|| { load_material( - shader::VERTEX, - shader::SECTOR_FRAGMENT, + ShaderSource::Glsl { vertex: shader::VERTEX, fragment: shader::SECTOR_FRAGMENT }, alpha_blend_material_params(vec![ - ("center".to_owned(), UniformType::Float2), - ("angle".to_owned(), UniformType::Float2), - ("blur".to_owned(), UniformType::Float2), + UniformDesc::new("center", UniformType::Float2), + UniformDesc::new("angle", UniformType::Float2), + UniformDesc::new("blur", UniformType::Float2), ]), ) .unwrap() @@ -60,12 +58,12 @@ impl Default for ShadowConfig { } impl ShadowConfig { - pub fn uniforms() -> Vec<(String, UniformType)> { + pub fn uniforms() -> Vec { vec![ - ("rect".to_owned(), UniformType::Float4), - ("elevation".to_owned(), UniformType::Float1), - ("radius".to_owned(), UniformType::Float1), - ("base".to_owned(), UniformType::Float1), + UniformDesc::new("rect", UniformType::Float4), + UniformDesc::new("elevation", UniformType::Float1), + UniformDesc::new("radius", UniformType::Float1), + UniformDesc::new("base", UniformType::Float1), ] } @@ -78,7 +76,7 @@ impl ShadowConfig { pub fn rounded_rect_shadow(ui: &mut Ui, r: Rect, config: &ShadowConfig) { // r.y += elevation * 0.5; - let mat = *SHADOW_MATERIAL; + let mat = SHADOW_MATERIAL.clone(); let gr = ui.rect_to_global(r); mat.set_uniform("rect", vec4(gr.x, gr.y, gr.right(), gr.bottom())); ShadowConfig { @@ -86,30 +84,30 @@ pub fn rounded_rect_shadow(ui: &mut Ui, r: Rect, config: &ShadowConfig) { ..*config } .apply(&mat); - gl_use_material(mat); + gl_use_material(&mat); let r3 = config.elevation * 3.0; draw_rectangle(gr.x - r3, gr.y - r3, gr.w + r3 * 2., gr.h + r3 * 2., WHITE); gl_use_default_material(); } pub fn clip_rounded_rect(ui: &mut Ui, r: Rect, radius: f32, f: impl FnOnce(&mut Ui) -> R) -> R { - let mat = *RR_MATERIAL; + let mat = RR_MATERIAL.clone(); let gr = ui.rect_to_global(r); mat.set_uniform("rect", vec4(gr.x, gr.y, gr.right(), gr.bottom())); mat.set_uniform("radius", radius); - gl_use_material(mat); + gl_use_material(&mat); let res = f(ui); gl_use_default_material(); res } pub fn clip_sector(ui: &mut Ui, ct: Vec2, start: f32, end: f32, f: impl FnOnce(&mut Ui) -> R) -> R { - let mat = *SECTOR_MATERIAL; + let mat = SECTOR_MATERIAL.clone(); mat.set_uniform("center", ui.to_global((ct.x, ct.y))); mat.set_uniform("angle", vec2(start, end)); let t = -end.sin(); mat.set_uniform("blur", vec2((ct.y - ui.top) / t, (ct.y + ui.top) / t)); - gl_use_material(mat); + gl_use_material(&mat); let res = f(ui); gl_use_default_material(); res diff --git a/prpr/src/ui/text.rs b/prpr/src/ui/text.rs index c3a98764..76a942af 100644 --- a/prpr/src/ui/text.rs +++ b/prpr/src/ui/text.rs @@ -8,7 +8,7 @@ use glyph_brush::{ BrushAction, BrushError, FontId, GlyphBrush, GlyphBrushBuilder, GlyphCruncher, HorizontalAlign, Layout, Section, SectionGlyph, Text, }; use macroquad::{ - miniquad::{Texture, TextureParams}, + miniquad::TextureParams, prelude::*, }; use once_cell::sync::Lazy; @@ -302,14 +302,15 @@ impl TextPainter { fn new_cache_texture(dim: (u32, u32)) -> Texture2D { debug!("creating cache texture: {}x{}", dim.0, dim.1); - Texture2D::from_miniquad_texture(Texture::new_render_texture( - unsafe { get_internal_gl() }.quad_context, + Texture2D::from_miniquad_texture(unsafe { get_internal_gl() }.quad_context.new_render_texture( TextureParams { width: dim.0, height: dim.1, - filter: FilterMode::Linear, + min_filter: FilterMode::Linear, + mag_filter: FilterMode::Linear, format: miniquad::TextureFormat::RGBA8, wrap: miniquad::TextureWrap::Clamp, + ..Default::default() }, )) } @@ -328,7 +329,8 @@ impl TextPainter { flushed = true; } use miniquad::gl::*; - glBindTexture(GL_TEXTURE_2D, self.cache_texture.raw_miniquad_texture_handle().gl_internal_id()); + let miniquad::RawId::OpenGl(gl_id) = get_internal_gl().quad_context.texture_raw_id(self.cache_texture.raw_miniquad_id()); + glBindTexture(GL_TEXTURE_2D, gl_id); self.data_buffer.clear(); self.data_buffer.reserve(tex_data.len() * 4); for alpha in tex_data { @@ -364,7 +366,7 @@ impl TextPainter { unsafe { get_internal_gl() }.flush(); flushed = true; } - self.cache_texture.delete(); + unsafe { get_internal_gl() }.quad_context.delete_texture(self.cache_texture.raw_miniquad_id()); self.cache_texture = Self::new_cache_texture(suggested); self.brush.resize_texture(suggested.0, suggested.1); } @@ -389,7 +391,7 @@ impl TextPainter { fn redraw(&self, tr: Matrix) { let gl = unsafe { get_internal_gl() }.quad_gl; - gl.texture(Some(self.cache_texture)); + gl.texture(Some(&self.cache_texture)); for vertices in self.vertices_buffer.chunks_exact(4) { let vertices = [ self.transform(&vertices[0], tr), @@ -404,6 +406,6 @@ impl TextPainter { impl Drop for TextPainter { fn drop(&mut self) { - self.cache_texture.delete(); + unsafe { get_internal_gl() }.quad_context.delete_texture(self.cache_texture.raw_miniquad_id()); } }