Compare commits
3
Commits
828986cd5c
...
4d116036b9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d116036b9 | ||
|
|
8073c97c5a | ||
|
|
0a9301f131 |
Generated
+56
-53
@@ -2756,9 +2756,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-base"
|
name = "gpui-base"
|
||||||
version = "0.6.6"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9e904ee0d351f4e85b967e26ae986d3aa195a8d9d35fcd18184a2c40989694e"
|
checksum = "64bb52b29a8dcdc8d3f595b8b0839c076f237572ab601ba4577d3da8ae085728"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -2772,9 +2772,11 @@ dependencies = [
|
|||||||
"gpui-pre-sum-tree",
|
"gpui-pre-sum-tree",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"instant",
|
"instant",
|
||||||
|
"itertools 0.13.0",
|
||||||
"lsp-types",
|
"lsp-types",
|
||||||
"markdown",
|
"markdown",
|
||||||
"markup5ever_rcdom",
|
"markup5ever_rcdom",
|
||||||
|
"num-traits",
|
||||||
"objc2 0.6.4",
|
"objc2 0.6.4",
|
||||||
"objc2-app-kit 0.3.2",
|
"objc2-app-kit 0.3.2",
|
||||||
"objc2-foundation 0.3.2",
|
"objc2-foundation 0.3.2",
|
||||||
@@ -2794,9 +2796,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre"
|
name = "gpui-pre"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0437c0b83e636a92bd1a39fa1d05fb632ae671289537497b35871ffbe231b84"
|
checksum = "0e87a42bb37c7cb4e76dd1ac0ce88851e46e976e0373a47ab3e0757abffee54d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -2864,12 +2866,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-apple"
|
name = "gpui-pre-apple"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3af534f0746eb9e014114bcc0ee00dc250c4b8d75df2e437180c403aa65649c1"
|
checksum = "00053551517815ab169fda28fff78dbebf2712722593c7671c511785cb3f79d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"block",
|
"block2 0.6.2",
|
||||||
"cbindgen",
|
"cbindgen",
|
||||||
"cocoa 0.26.1",
|
"cocoa 0.26.1",
|
||||||
"core-foundation 0.10.1",
|
"core-foundation 0.10.1",
|
||||||
@@ -2888,9 +2890,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-collections"
|
name = "gpui-pre-collections"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "15e7a65204039187d1f4b3d4c2e0233eeac54d4acb0b13f9dc1a3b5adad26c1f"
|
checksum = "f79760925c31ed06924d28d68981b5b22b2ce25665f58f0a33dfea11170e6e28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui-pre-util",
|
"gpui-pre-util",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
@@ -2899,9 +2901,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-derive-refineable"
|
name = "gpui-pre-derive-refineable"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79dfb4a05f3cd9893d7768dbf5dba1bbbe99cab7394e89b360ccb3b2bcb6b34e"
|
checksum = "d1aea3f941581a6a9dd545ecd351ef2d336fe6e2a1a4555cb6414944a4984260"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2910,9 +2912,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-http-client"
|
name = "gpui-pre-http-client"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cfd5a7542c77887a5aff997d78c95884119c8463409569b63fcf060956da1682"
|
checksum = "ddce27e989e877a3a1a243f97d7ea4f1ade2532abec418d5a0be9cca802702c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-compression",
|
"async-compression",
|
||||||
@@ -2931,9 +2933,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-http-client-tls"
|
name = "gpui-pre-http-client-tls"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72aed510adb5e7723f8f57b9751007e3451d46a84edb59b92227178cf59cc19c"
|
checksum = "0076f6cf87b87b5629d55692638df4edc04fab3da92f897c2294d520efac17d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"rustls",
|
"rustls",
|
||||||
@@ -2943,9 +2945,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-linux"
|
name = "gpui-pre-linux"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aab655cda1d6dcd549ac869e8678babc84cf573ab6794a1fe1a39c6c70c60b48"
|
checksum = "0aac7022e347409454777701082201742710052813964a1e25160f7e22c965ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_unix",
|
"accesskit_unix",
|
||||||
@@ -2990,9 +2992,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-macos"
|
name = "gpui-pre-macos"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "951d17e41a72067ad2d35c60e08a62e8ad7fa9400b41c802e26cfb459f6c05da"
|
checksum = "5a43af845b260b09393e923c4f1e1a67a10fcfc847b4815192bbfd02ed9fe725"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_macos",
|
"accesskit_macos",
|
||||||
@@ -3037,9 +3039,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-macros"
|
name = "gpui-pre-macros"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6324092b40ea8ec7c327a4a28427e366a489b4d5d23b5d1bd0243fc2f4ca7a9e"
|
checksum = "e1627c5dd3c3351e39e0621d3f3e31dbbe7c7658df90b1d2ec12bfe95d54cd37"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
@@ -3050,9 +3052,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-perf"
|
name = "gpui-pre-perf"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b94bff26ccd8105f6206144f8dab16a2dc02e7a68f39dada02cdf492f0afd940"
|
checksum = "01954497dd02ba96ab4c252a8668643b0cc3085e7a75dca62e7ba0e3dcd83b02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui-pre-collections",
|
"gpui-pre-collections",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -3061,10 +3063,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-platform"
|
name = "gpui-pre-platform"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8423d1e0a693d2f9f16c0326bb01b5e81ec0531854d596a18476934ac6880478"
|
checksum = "0112105c2ac6757fbf527c9c92a753be9bcbce9f4cf26959a11eaa1db6565aa5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"gpui-pre",
|
"gpui-pre",
|
||||||
"gpui-pre-linux",
|
"gpui-pre-linux",
|
||||||
@@ -3075,9 +3078,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-refineable"
|
name = "gpui-pre-refineable"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "93b4b0a8d296a3ee5beb1136bc48f8167a6028eb1e243b56851dcd8c9fc6d7a1"
|
checksum = "ffd14639e5dfab906bbaeabfe0f94289120dcf2e6ed37217deb13964867e89a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui-pre-derive-refineable",
|
"gpui-pre-derive-refineable",
|
||||||
]
|
]
|
||||||
@@ -3134,9 +3137,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-reqwest-client"
|
name = "gpui-pre-reqwest-client"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f32ccea5d1a7c8d2868d7b346c2c1f76aa3f86ec9e0575c4e7340af3afd9e9d"
|
checksum = "53fa47d680f9e186b33af323f97345d67b6150c0e4c0bc894406bff822a4d9b4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -3152,9 +3155,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-scheduler"
|
name = "gpui-pre-scheduler"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff7b10b3d2fc3fda24847a04436eca9531612c8414874a37ddc005f20e521e12"
|
checksum = "8049963b716df01b67b58f86000597f222d5b31b15b35069b23a3b07f3c68352"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-task",
|
"async-task",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
@@ -3169,9 +3172,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-shared-string"
|
name = "gpui-pre-shared-string"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe17f81bd98a0253140ea79f7f76442d7499bd8d17db302861af8e46c11d38df"
|
checksum = "8ef6314bdf5ab162c1913712ec4a91b013ce2a9cfb0b557ecdfdd5793c8b61d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -3180,9 +3183,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-sum-tree"
|
name = "gpui-pre-sum-tree"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a6d35e998a51d24248dad731de1f09d386cbbf46a04edde3b67f469b2ce3f16"
|
checksum = "5cca7e5fad9d53b2d265860fefdffc5f171966cb99c3fd99ecac15c033f19209"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui-pre-ztracing",
|
"gpui-pre-ztracing",
|
||||||
"heapless 0.9.3",
|
"heapless 0.9.3",
|
||||||
@@ -3193,9 +3196,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-util"
|
name = "gpui-pre-util"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a026a30c578930d20974fd8c3d77de36223ac7ba193bd4259e7238929bc8806"
|
checksum = "d82e06359ae76f8bb07ed713c54adb80cea6a9a2d3dc715f41aec30cd2aadb4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"log",
|
"log",
|
||||||
@@ -3204,9 +3207,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-util-macros"
|
name = "gpui-pre-util-macros"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5cbf79627a6afc626d4e887dad9d2d92bb069413e8cc692ba3e90bff219eb7a9"
|
checksum = "b8a0da4143bb0e7eb926eed7edd3a61c9d17344207543dcc25fb398f0cb96b2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui-pre-perf",
|
"gpui-pre-perf",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -3215,9 +3218,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-web"
|
name = "gpui-pre-web"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34be433b9fcb370b1fe9a1c7fe911f0eb6058791ef8167dcf6c5a5deb624975c"
|
checksum = "ab3870cc909471bb830c3d79496770c754401aad6cfa122147ccb13e8908ad08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
@@ -3244,9 +3247,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-wgpu"
|
name = "gpui-pre-wgpu"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05515b32799b1767d69300921c1b562cd53104965211a0e8905acdc7aa18e693"
|
checksum = "f0b02657b56b09140ce542f5e4434fb96d9fdaba0fd0035ba7dba13c171ddb9b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
@@ -3271,9 +3274,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-windows"
|
name = "gpui-pre-windows"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "27662e65bfcf4445d2cccfad91f1570ec07f4b1b5de98400a9f207fd19c464cf"
|
checksum = "f05592a6f9e3e6bb7a9f2a0d4779271cf747a948db1c07b02448de777ffff8f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_windows",
|
"accesskit_windows",
|
||||||
@@ -3300,9 +3303,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-zlog"
|
name = "gpui-pre-zlog"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3a5af0b734a391a84872eb770b3ee9830091ec2487b79d06371fe14eefb2b5d"
|
checksum = "a7506302091e66d7fb5e04a5b3480e90cff0bbe3157458d7df91a41c59238c48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3312,9 +3315,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-ztracing"
|
name = "gpui-pre-ztracing"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a24cb19f78c5bc3aab8ddf1e8e0bd6b402fbcb9c7f112f64ddbedef8f8768a59"
|
checksum = "4ed1bc6d53b3ca93785a8573d1066d555b67bc475b344fb28871f9811eb6f218"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui-pre-zlog",
|
"gpui-pre-zlog",
|
||||||
"gpui-pre-ztracing-macro",
|
"gpui-pre-ztracing-macro",
|
||||||
@@ -3324,9 +3327,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-pre-ztracing-macro"
|
name = "gpui-pre-ztracing-macro"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1bb25850ad31b88bbdef454afb93ec4cdb81f5e200ba5b3f1a0368111780d2c"
|
checksum = "829258540ea51bb9ea4a71686beffe5c99586901ba9f39944d06e4a180ab63fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-updater-core"
|
name = "gpui-updater-core"
|
||||||
@@ -7685,9 +7688,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.26.5"
|
version = "0.26.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67"
|
checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls",
|
"rustls",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.5" }
|
|||||||
gpui_tokio = { path = "crates/gpui_tokio" }
|
gpui_tokio = { path = "crates/gpui_tokio" }
|
||||||
|
|
||||||
# Unstyled behavior, state, and infrastructure from GPUI Kit
|
# Unstyled behavior, state, and infrastructure from GPUI Kit
|
||||||
gpui-base = "0.6.1"
|
gpui-base = "0.7.0"
|
||||||
|
|
||||||
# Nostr
|
# Nostr
|
||||||
nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" }
|
nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" }
|
||||||
|
|||||||
@@ -299,9 +299,9 @@ impl Room {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A stable seed for the room's generated avatar
|
/// A stable seed for the room's generated avatar
|
||||||
pub fn display_image_seed(&self, cx: &App) -> SharedString {
|
pub fn display_image_seed(&self, cx: &App) -> String {
|
||||||
if self.is_group() {
|
if self.is_group() {
|
||||||
SharedString::from(self.id.to_string())
|
self.id.to_string()
|
||||||
} else {
|
} else {
|
||||||
self.display_member(cx).avatar_seed()
|
self.display_member(cx).avatar_seed()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -633,7 +633,7 @@ impl ChatPanel {
|
|||||||
self.reports_by_id.read().unwrap().contains_key(id)
|
self.reports_by_id.read().unwrap().contains_key(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clone reports for a message (used for modal display, not called during render)
|
/// Clone reports for a message (used for dialog display, not called during render)
|
||||||
fn sent_reports(&self, id: &EventId) -> Option<Vec<SendReport>> {
|
fn sent_reports(&self, id: &EventId) -> Option<Vec<SendReport>> {
|
||||||
self.reports_by_id.read().unwrap().get(id).cloned()
|
self.reports_by_id.read().unwrap().get(id).cloned()
|
||||||
}
|
}
|
||||||
@@ -978,7 +978,7 @@ impl ChatPanel {
|
|||||||
let chat = ChatRegistry::global(cx);
|
let chat = ChatRegistry::global(cx);
|
||||||
let seen_on = chat.read(cx).rumor_seen_on(id);
|
let seen_on = chat.read(cx).rumor_seen_on(id);
|
||||||
|
|
||||||
window.open_modal(cx, move |this, _window, cx| {
|
window.open_dialog(cx, move |this, _window, cx| {
|
||||||
this.title("Seen on").show_close(true).child(
|
this.title("Seen on").show_close(true).child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
@@ -1021,7 +1021,7 @@ impl ChatPanel {
|
|||||||
fn open_relays(&mut self, public_key: &PublicKey, window: &mut Window, cx: &mut Context<Self>) {
|
fn open_relays(&mut self, public_key: &PublicKey, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let profile = self.profile(public_key, cx);
|
let profile = self.profile(public_key, cx);
|
||||||
|
|
||||||
window.open_modal(cx, move |this, _window, cx| {
|
window.open_dialog(cx, move |this, _window, cx| {
|
||||||
let relays = profile.messaging_relays();
|
let relays = profile.messaging_relays();
|
||||||
|
|
||||||
this.title("Messaging Relays")
|
this.title("Messaging Relays")
|
||||||
@@ -1422,7 +1422,7 @@ impl ChatPanel {
|
|||||||
this.on_click(move |_e, window, cx| {
|
this.on_click(move |_e, window, cx| {
|
||||||
let reports = reports.clone();
|
let reports = reports.clone();
|
||||||
|
|
||||||
window.open_modal(cx, move |this, _window, cx| {
|
window.open_dialog(cx, move |this, _window, cx| {
|
||||||
this.title(SharedString::from("Sent Reports"))
|
this.title(SharedString::from("Sent Reports"))
|
||||||
.show_close(true)
|
.show_close(true)
|
||||||
.child(v_flex().gap_4().children({
|
.child(v_flex().gap_4().children({
|
||||||
|
|||||||
@@ -641,10 +641,7 @@ impl CommunityPanel {
|
|||||||
.community
|
.community
|
||||||
.read_with(cx, |community, _cx| {
|
.read_with(cx, |community, _cx| {
|
||||||
let seed = community.id().to_hex();
|
let seed = community.id().to_hex();
|
||||||
let avatar = match community.icon() {
|
let avatar = Avatar::from_source(community.icon()).seed(seed).large();
|
||||||
Some(path) => Avatar::from_source(path).seed(seed).large(),
|
|
||||||
None => Avatar::new(None).seed(seed).large(),
|
|
||||||
};
|
|
||||||
|
|
||||||
(community.name(), avatar)
|
(community.name(), avatar)
|
||||||
})
|
})
|
||||||
@@ -762,10 +759,7 @@ impl Panel for CommunityPanel {
|
|||||||
self.community
|
self.community
|
||||||
.read_with(cx, |community, _cx| {
|
.read_with(cx, |community, _cx| {
|
||||||
let seed = community.id().to_hex();
|
let seed = community.id().to_hex();
|
||||||
let avatar = match community.icon() {
|
let avatar = Avatar::from_source(community.icon()).seed(seed).xsmall();
|
||||||
Some(path) => Avatar::from_source(path).seed(seed).xsmall(),
|
|
||||||
None => Avatar::new(None).seed(seed).xsmall(),
|
|
||||||
};
|
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
|
|||||||
@@ -235,6 +235,17 @@ impl CommunityState {
|
|||||||
None => BTreeMap::new(),
|
None => BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let relays = material
|
||||||
|
.relays
|
||||||
|
.iter()
|
||||||
|
.filter_map(|relay| RelayUrl::parse(relay).ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let control_root = match &material.control_root {
|
||||||
|
Some(root) => Some(decode_hex_32(root)?),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
let mut channels = Vec::with_capacity(material.channels.len());
|
let mut channels = Vec::with_capacity(material.channels.len());
|
||||||
|
|
||||||
for grant in &material.channels {
|
for grant in &material.channels {
|
||||||
@@ -260,17 +271,10 @@ impl CommunityState {
|
|||||||
owner_salt: decode_hex_32(&material.owner_salt)?,
|
owner_salt: decode_hex_32(&material.owner_salt)?,
|
||||||
community_root: decode_hex_32(&material.community_root)?,
|
community_root: decode_hex_32(&material.community_root)?,
|
||||||
root_epoch: material.root_epoch,
|
root_epoch: material.root_epoch,
|
||||||
control_root: match &material.control_root {
|
control_root,
|
||||||
Some(root) => Some(decode_hex_32(root)?),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
control_pks,
|
control_pks,
|
||||||
channels,
|
channels,
|
||||||
relays: material
|
relays,
|
||||||
.relays
|
|
||||||
.iter()
|
|
||||||
.filter_map(|relay| RelayUrl::parse(relay).ok())
|
|
||||||
.collect(),
|
|
||||||
heads: Vec::new(),
|
heads: Vec::new(),
|
||||||
banned: BTreeSet::new(),
|
banned: BTreeSet::new(),
|
||||||
cursors: BTreeMap::new(),
|
cursors: BTreeMap::new(),
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ impl Person {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A stable seed for this profile's generated avatar
|
/// A stable seed for this profile's generated avatar
|
||||||
pub fn avatar_seed(&self) -> SharedString {
|
pub fn avatar_seed(&self) -> String {
|
||||||
SharedString::from(self.public_key().to_hex())
|
self.public_key().to_hex()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get profile name
|
/// Get profile name
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ pub struct Theme {
|
|||||||
/// Radius for the general elements.
|
/// Radius for the general elements.
|
||||||
pub radius: Pixels,
|
pub radius: Pixels,
|
||||||
|
|
||||||
/// Radius for the large elements, e.g.: modal, notification.
|
/// Radius for the large elements, e.g.: dialog, notification.
|
||||||
pub radius_lg: Pixels,
|
pub radius_lg: Pixels,
|
||||||
|
|
||||||
/// Enable shadow for the general elements. default is true
|
/// Enable shadow for the general elements. default is true
|
||||||
|
|||||||
+24
-132
@@ -10,7 +10,10 @@ use crate::{Selectable, Sizable, Size, StyledExt};
|
|||||||
|
|
||||||
/// Number of rows and columns in the generated pixel grid.
|
/// Number of rows and columns in the generated pixel grid.
|
||||||
const PIXEL_GRID: usize = 8;
|
const PIXEL_GRID: usize = 8;
|
||||||
/// Probability that a cell in the left half of the grid is filled.
|
/// Empty cells kept between the pattern and the avatar edge, so the art
|
||||||
|
/// gathers in the center instead of filling the whole avatar.
|
||||||
|
const MARGIN: usize = 1;
|
||||||
|
/// Probability that a cell in the left half of the pattern area is filled.
|
||||||
const FILL_PROBABILITY: f32 = 0.42;
|
const FILL_PROBABILITY: f32 = 0.42;
|
||||||
/// Probability that a filled cell uses the accent shade instead of the main color.
|
/// Probability that a filled cell uses the accent shade instead of the main color.
|
||||||
const ACCENT_PROBABILITY: f32 = 0.25;
|
const ACCENT_PROBABILITY: f32 = 0.25;
|
||||||
@@ -33,19 +36,6 @@ pub(super) fn avatar_size(size: Size) -> AbsoluteLength {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A deterministic, offline pixel-art avatar derived from a seed.
|
/// A deterministic, offline pixel-art avatar derived from a seed.
|
||||||
///
|
|
||||||
/// Use it for entities that have no profile picture: the same seed always
|
|
||||||
/// renders the same pattern, so identities stay recognizable without a
|
|
||||||
/// network round trip. The pattern is painted as geometry and cropped to a
|
|
||||||
/// circle, at the same sizes as [`Avatar`].
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use ui::avatar::PixelAvatar;
|
|
||||||
///
|
|
||||||
/// PixelAvatar::new("alice");
|
|
||||||
/// ```
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct PixelAvatar {
|
pub struct PixelAvatar {
|
||||||
seed: u64,
|
seed: u64,
|
||||||
@@ -87,7 +77,7 @@ impl RenderOnce for PixelAvatar {
|
|||||||
move |bounds, seed, window, cx| {
|
move |bounds, seed, window, cx| {
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
let main = Hsla {
|
let main = Hsla {
|
||||||
h: (theme.icon_accent.h + seed as f32 / u64::MAX as f32) % 1.,
|
h: seed as f32 / u64::MAX as f32,
|
||||||
s: 0.6,
|
s: 0.6,
|
||||||
l: if theme.is_dark() { 0.6 } else { 0.45 },
|
l: if theme.is_dark() { 0.6 } else { 0.45 },
|
||||||
a: 1.,
|
a: 1.,
|
||||||
@@ -142,8 +132,11 @@ fn pixel_pattern(seed: u64) -> [u8; PIXEL_GRID * PIXEL_GRID] {
|
|||||||
let mut pattern = [0u8; PIXEL_GRID * PIXEL_GRID];
|
let mut pattern = [0u8; PIXEL_GRID * PIXEL_GRID];
|
||||||
let mut filled = 0usize;
|
let mut filled = 0usize;
|
||||||
|
|
||||||
for row in 0..PIXEL_GRID {
|
let art_rows = PIXEL_GRID - 2 * MARGIN;
|
||||||
for col in 0..PIXEL_GRID / 2 {
|
let art_columns = PIXEL_GRID / 2 - MARGIN;
|
||||||
|
|
||||||
|
for row in MARGIN..PIXEL_GRID - MARGIN {
|
||||||
|
for col in MARGIN..PIXEL_GRID / 2 {
|
||||||
if rng.chance(FILL_PROBABILITY) {
|
if rng.chance(FILL_PROBABILITY) {
|
||||||
let accent = rng.chance(ACCENT_PROBABILITY);
|
let accent = rng.chance(ACCENT_PROBABILITY);
|
||||||
set_cell(&mut pattern, row, col, if accent { 2 } else { 1 });
|
set_cell(&mut pattern, row, col, if accent { 2 } else { 1 });
|
||||||
@@ -153,17 +146,17 @@ fn pixel_pattern(seed: u64) -> [u8; PIXEL_GRID * PIXEL_GRID] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if filled < MIN_FILLED {
|
if filled < MIN_FILLED {
|
||||||
let half = PIXEL_GRID * PIXEL_GRID / 2;
|
let total = art_rows * art_columns;
|
||||||
let start = (rng.next() % half as u64) as usize;
|
let start = (rng.next() % total as u64) as usize;
|
||||||
|
|
||||||
for offset in 0..half {
|
for offset in 0..total {
|
||||||
if filled >= MIN_FILLED {
|
if filled >= MIN_FILLED {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ix = (start + offset) % half;
|
let ix = (start + offset) % total;
|
||||||
let row = ix / (PIXEL_GRID / 2);
|
let row = MARGIN + ix / art_columns;
|
||||||
let col = ix % (PIXEL_GRID / 2);
|
let col = MARGIN + ix % art_columns;
|
||||||
|
|
||||||
if pattern[row * PIXEL_GRID + col] == 0 {
|
if pattern[row * PIXEL_GRID + col] == 0 {
|
||||||
set_cell(&mut pattern, row, col, 1);
|
set_cell(&mut pattern, row, col, 1);
|
||||||
@@ -358,24 +351,12 @@ fn generated_avatar(seed: Option<&str>, size: Pixels) -> AnyElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An element that renders a user avatar with customizable appearance options.
|
/// An element that renders a user avatar with customizable appearance options.
|
||||||
///
|
|
||||||
/// Entities without a picture still get a stable identity: the avatar falls
|
|
||||||
/// back to a [`PixelAvatar`] seeded through [`Avatar::seed`], both when there
|
|
||||||
/// is no picture and when the picture fails to load.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use ui::avatar::Avatar;
|
|
||||||
///
|
|
||||||
/// Avatar::new(None).seed("alice");
|
|
||||||
/// ```
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct Avatar {
|
pub struct Avatar {
|
||||||
base: Div,
|
base: Div,
|
||||||
picture: Option<ImageSource>,
|
picture: Option<ImageSource>,
|
||||||
grayscale: bool,
|
grayscale: bool,
|
||||||
seed: Option<SharedString>,
|
seed: Option<String>,
|
||||||
style: StyleRefinement,
|
style: StyleRefinement,
|
||||||
size: Size,
|
size: Size,
|
||||||
border_color: Option<Hsla>,
|
border_color: Option<Hsla>,
|
||||||
@@ -384,16 +365,13 @@ pub struct Avatar {
|
|||||||
|
|
||||||
impl Avatar {
|
impl Avatar {
|
||||||
/// Creates an avatar for an entity whose profile picture may be missing.
|
/// Creates an avatar for an entity whose profile picture may be missing.
|
||||||
///
|
|
||||||
/// Use [`Avatar::seed`] to choose the generated
|
|
||||||
/// pixel avatar rendered when `picture` is `None`.
|
|
||||||
pub fn new(picture: Option<SharedString>) -> Self {
|
pub fn new(picture: Option<SharedString>) -> Self {
|
||||||
Self::from_picture(picture.map(ImageSource::from))
|
Self::from_source(picture)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an avatar from an already-resolved source.
|
/// Creates an avatar from an already-resolved source, which may be missing.
|
||||||
pub fn from_source(picture: impl Into<ImageSource>) -> Self {
|
pub fn from_source(picture: Option<impl Into<ImageSource>>) -> Self {
|
||||||
Self::from_picture(Some(picture.into()))
|
Self::from_picture(picture.map(Into::into))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_picture(picture: Option<ImageSource>) -> Self {
|
fn from_picture(picture: Option<ImageSource>) -> Self {
|
||||||
@@ -410,33 +388,18 @@ impl Avatar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the seed for the generated pixel avatar.
|
/// Sets the seed for the generated pixel avatar.
|
||||||
///
|
pub fn seed(mut self, seed: impl Into<String>) -> Self {
|
||||||
/// The seed should be a stable identifier of the entity the avatar
|
|
||||||
/// represents, such as a public key.
|
|
||||||
pub fn seed(mut self, seed: impl Into<SharedString>) -> Self {
|
|
||||||
self.seed = Some(seed.into());
|
self.seed = Some(seed.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies a grayscale filter to the avatar image.
|
/// Applies a grayscale filter to the avatar image.
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use ui::avatar::Avatar;
|
|
||||||
///
|
|
||||||
/// Avatar::new(None).grayscale(true);
|
|
||||||
/// ```
|
|
||||||
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
||||||
self.grayscale = grayscale;
|
self.grayscale = grayscale;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the border color of the avatar.
|
/// Sets the border color of the avatar.
|
||||||
///
|
|
||||||
/// This might be used to match the border to the background color of
|
|
||||||
/// the parent element to create the illusion of cropping another
|
|
||||||
/// shape underneath (for example in face piles.)
|
|
||||||
pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
|
pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
|
||||||
self.border_color = Some(color.into());
|
self.border_color = Some(color.into());
|
||||||
self
|
self
|
||||||
@@ -480,6 +443,7 @@ impl RenderOnce for Avatar {
|
|||||||
} else {
|
} else {
|
||||||
px(0.)
|
px(0.)
|
||||||
};
|
};
|
||||||
|
|
||||||
let image_size = avatar_size(self.size).to_pixels(window.rem_size());
|
let image_size = avatar_size(self.size).to_pixels(window.rem_size());
|
||||||
let container_size = image_size + border_width * 2.;
|
let container_size = image_size + border_width * 2.;
|
||||||
|
|
||||||
@@ -487,6 +451,7 @@ impl RenderOnce for Avatar {
|
|||||||
Some(picture) => {
|
Some(picture) => {
|
||||||
let seed = self.seed;
|
let seed = self.seed;
|
||||||
let grayscale = self.grayscale;
|
let grayscale = self.grayscale;
|
||||||
|
|
||||||
img(picture)
|
img(picture)
|
||||||
.size(image_size)
|
.size(image_size)
|
||||||
.rounded_full()
|
.rounded_full()
|
||||||
@@ -510,76 +475,3 @@ impl RenderOnce for Avatar {
|
|||||||
.child(content)
|
.child(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pixel_patterns_are_symmetric_and_stable() {
|
|
||||||
for seed in 0..50 {
|
|
||||||
let pattern = pixel_pattern(seed);
|
|
||||||
let filled = pattern.iter().filter(|&&cell| cell != 0).count();
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
filled >= MIN_FILLED * 2,
|
|
||||||
"pattern too sparse for seed {seed}"
|
|
||||||
);
|
|
||||||
|
|
||||||
for row in 0..PIXEL_GRID {
|
|
||||||
for col in 0..PIXEL_GRID {
|
|
||||||
assert_eq!(
|
|
||||||
pattern[row * PIXEL_GRID + col],
|
|
||||||
pattern[row * PIXEL_GRID + (PIXEL_GRID - 1 - col)],
|
|
||||||
"asymmetric pattern for seed {seed} at ({row}, {col})"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for seed in [0, 1, 42, u64::MAX] {
|
|
||||||
assert_eq!(pixel_pattern(seed), pixel_pattern(seed));
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_ne!(pixel_pattern(42), pixel_pattern(43));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn area(polygon: &[Point<Pixels>]) -> f32 {
|
|
||||||
let mut sum: f32 = 0.;
|
|
||||||
for (&a, &b) in polygon.iter().zip(polygon.iter().cycle().skip(1)) {
|
|
||||||
sum += a.x.as_f32() * b.y.as_f32() - b.x.as_f32() * a.y.as_f32();
|
|
||||||
}
|
|
||||||
(sum / 2.).abs()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn clipping_keeps_only_the_part_inside_the_circle() {
|
|
||||||
let circle = circle_polygon(point(px(10.), px(10.)), 10.);
|
|
||||||
let square = |left: f32, top: f32| {
|
|
||||||
[
|
|
||||||
point(px(left), px(top)),
|
|
||||||
point(px(left + 4.), px(top)),
|
|
||||||
point(px(left + 4.), px(top + 4.)),
|
|
||||||
point(px(left), px(top + 4.)),
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
let inside = clip_polygon(&square(8., 8.), &circle);
|
|
||||||
assert!((area(&inside) - 16.).abs() < 0.05, "area {}", area(&inside));
|
|
||||||
|
|
||||||
assert!(clip_polygon(&square(20., 20.), &circle).is_empty());
|
|
||||||
|
|
||||||
let straddling = clip_polygon(&square(0., 0.), &circle);
|
|
||||||
for vertex in &straddling {
|
|
||||||
let delta_x = vertex.x.as_f32() - 10.;
|
|
||||||
let delta_y = vertex.y.as_f32() - 10.;
|
|
||||||
assert!(
|
|
||||||
delta_x.hypot(delta_y) <= 10. + 0.1,
|
|
||||||
"clipped vertex outside the circle"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let area = area(&straddling);
|
|
||||||
assert!(area > 0. && area < 16., "area {area}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use gpui::{
|
|||||||
InteractiveElement as _, IntoElement, ParentElement, Pixels, RenderOnce, SharedString,
|
InteractiveElement as _, IntoElement, ParentElement, Pixels, RenderOnce, SharedString,
|
||||||
StyleRefinement, Styled, Window, div, hsla, point, px, size,
|
StyleRefinement, Styled, Window, div, hsla, point, px, size,
|
||||||
};
|
};
|
||||||
use gpui_base::Dialog;
|
|
||||||
use instant::Duration;
|
use instant::Duration;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
|
||||||
@@ -22,15 +21,15 @@ type RenderButtonFn = Box<dyn FnOnce(&mut Window, &mut App) -> AnyElement>;
|
|||||||
type FooterFn =
|
type FooterFn =
|
||||||
Box<dyn Fn(RenderButtonFn, RenderButtonFn, &mut Window, &mut App) -> Vec<AnyElement>>;
|
Box<dyn Fn(RenderButtonFn, RenderButtonFn, &mut Window, &mut App) -> Vec<AnyElement>>;
|
||||||
|
|
||||||
/// Modal button props.
|
/// Dialog button props.
|
||||||
pub struct ModalButtonProps {
|
pub struct DialogButtonProps {
|
||||||
ok_text: Option<SharedString>,
|
ok_text: Option<SharedString>,
|
||||||
ok_variant: ButtonVariant,
|
ok_variant: ButtonVariant,
|
||||||
cancel_text: Option<SharedString>,
|
cancel_text: Option<SharedString>,
|
||||||
cancel_variant: ButtonVariant,
|
cancel_variant: ButtonVariant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ModalButtonProps {
|
impl Default for DialogButtonProps {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ok_text: None,
|
ok_text: None,
|
||||||
@@ -41,7 +40,7 @@ impl Default for ModalButtonProps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModalButtonProps {
|
impl DialogButtonProps {
|
||||||
/// Sets the text of the OK button. Default is `OK`.
|
/// Sets the text of the OK button. Default is `OK`.
|
||||||
pub fn ok_text(mut self, ok_text: impl Into<SharedString>) -> Self {
|
pub fn ok_text(mut self, ok_text: impl Into<SharedString>) -> Self {
|
||||||
self.ok_text = Some(ok_text.into());
|
self.ok_text = Some(ok_text.into());
|
||||||
@@ -68,7 +67,7 @@ impl ModalButtonProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct Modal {
|
pub struct Dialog {
|
||||||
style: StyleRefinement,
|
style: StyleRefinement,
|
||||||
title: Option<AnyElement>,
|
title: Option<AnyElement>,
|
||||||
footer: Option<FooterFn>,
|
footer: Option<FooterFn>,
|
||||||
@@ -85,15 +84,15 @@ pub struct Modal {
|
|||||||
overlay_closable: bool,
|
overlay_closable: bool,
|
||||||
keyboard: bool,
|
keyboard: bool,
|
||||||
show_close: bool,
|
show_close: bool,
|
||||||
button_props: ModalButtonProps,
|
button_props: DialogButtonProps,
|
||||||
|
|
||||||
/// This will be change when open the modal, the focus handle is create when open the modal.
|
/// This will be change when open the dialog, the focus handle is create when open the dialog.
|
||||||
pub focus_handle: FocusHandle,
|
pub focus_handle: FocusHandle,
|
||||||
pub layer_ix: usize,
|
pub layer_ix: usize,
|
||||||
pub overlay_visible: bool,
|
pub overlay_visible: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Modal {
|
impl Dialog {
|
||||||
pub fn new(_window: &mut Window, cx: &mut App) -> Self {
|
pub fn new(_window: &mut Window, cx: &mut App) -> Self {
|
||||||
Self {
|
Self {
|
||||||
style: StyleRefinement::default(),
|
style: StyleRefinement::default(),
|
||||||
@@ -111,19 +110,19 @@ impl Modal {
|
|||||||
on_close: Rc::new(|_, _, _| {}),
|
on_close: Rc::new(|_, _, _| {}),
|
||||||
on_ok: None,
|
on_ok: None,
|
||||||
on_cancel: Rc::new(|_, _, _| true),
|
on_cancel: Rc::new(|_, _, _| true),
|
||||||
button_props: ModalButtonProps::default(),
|
button_props: DialogButtonProps::default(),
|
||||||
show_close: true,
|
show_close: true,
|
||||||
overlay_closable: true,
|
overlay_closable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the title of the modal.
|
/// Sets the title of the dialog.
|
||||||
pub fn title(mut self, title: impl IntoElement) -> Self {
|
pub fn title(mut self, title: impl IntoElement) -> Self {
|
||||||
self.title = Some(title.into_any_element());
|
self.title = Some(title.into_any_element());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the footer of the modal.
|
/// Set the footer of the dialog.
|
||||||
///
|
///
|
||||||
/// The `footer` is a function that takes two `RenderButtonFn` and a `WindowContext` and returns a list of `AnyElement`.
|
/// The `footer` is a function that takes two `RenderButtonFn` and a `WindowContext` and returns a list of `AnyElement`.
|
||||||
///
|
///
|
||||||
@@ -145,7 +144,7 @@ impl Modal {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set to use confirm modal, with OK and Cancel buttons.
|
/// Set to use confirm dialog, with OK and Cancel buttons.
|
||||||
///
|
///
|
||||||
/// See also [`Self::alert`]
|
/// See also [`Self::alert`]
|
||||||
pub fn confirm(self) -> Self {
|
pub fn confirm(self) -> Self {
|
||||||
@@ -154,7 +153,7 @@ impl Modal {
|
|||||||
.show_close(false)
|
.show_close(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set to as a alter modal, with OK button.
|
/// Set to as a alter dialog, with OK button.
|
||||||
///
|
///
|
||||||
/// See also [`Self::confirm`]
|
/// See also [`Self::confirm`]
|
||||||
pub fn alert(self) -> Self {
|
pub fn alert(self) -> Self {
|
||||||
@@ -163,13 +162,13 @@ impl Modal {
|
|||||||
.show_close(false)
|
.show_close(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the button props of the modal.
|
/// Set the button props of the dialog.
|
||||||
pub fn button_props(mut self, button_props: ModalButtonProps) -> Self {
|
pub fn button_props(mut self, button_props: DialogButtonProps) -> Self {
|
||||||
self.button_props = button_props;
|
self.button_props = button_props;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the callback for when the modal is closed.
|
/// Sets the callback for when the dialog is closed.
|
||||||
///
|
///
|
||||||
/// Called after [`Self::on_ok`] or [`Self::on_cancel`] callback.
|
/// Called after [`Self::on_ok`] or [`Self::on_cancel`] callback.
|
||||||
pub fn on_close(
|
pub fn on_close(
|
||||||
@@ -180,9 +179,9 @@ impl Modal {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the callback for when the modal is has been confirmed.
|
/// Sets the callback for when the dialog is has been confirmed.
|
||||||
///
|
///
|
||||||
/// The callback should return `true` to close the modal, if return `false` the modal will not be closed.
|
/// The callback should return `true` to close the dialog, if return `false` the dialog will not be closed.
|
||||||
pub fn on_ok(
|
pub fn on_ok(
|
||||||
mut self,
|
mut self,
|
||||||
on_ok: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static,
|
on_ok: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static,
|
||||||
@@ -191,9 +190,9 @@ impl Modal {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the callback for when the modal is has been canceled.
|
/// Sets the callback for when the dialog is has been canceled.
|
||||||
///
|
///
|
||||||
/// The callback should return `true` to close the modal, if return `false` the modal will not be closed.
|
/// The callback should return `true` to close the dialog, if return `false` the dialog will not be closed.
|
||||||
pub fn on_cancel(
|
pub fn on_cancel(
|
||||||
mut self,
|
mut self,
|
||||||
on_cancel: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static,
|
on_cancel: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static,
|
||||||
@@ -208,39 +207,39 @@ impl Modal {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the top offset of the modal, defaults to None, will use the 1/10 of the viewport height.
|
/// Shift the dialog down from the window centre, defaults to `None`.
|
||||||
pub fn margin_top(mut self, margin_top: Pixels) -> Self {
|
pub fn margin_top(mut self, margin_top: Pixels) -> Self {
|
||||||
self.margin_top = Some(margin_top);
|
self.margin_top = Some(margin_top);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the width of the modal, defaults to 480px.
|
/// Sets the width of the dialog, defaults to 480px.
|
||||||
pub fn width(mut self, width: Pixels) -> Self {
|
pub fn width(mut self, width: Pixels) -> Self {
|
||||||
self.width = width;
|
self.width = width;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum width of the modal, defaults to `None`.
|
/// Set the maximum width of the dialog, defaults to `None`.
|
||||||
pub fn max_w(mut self, max_width: Pixels) -> Self {
|
pub fn max_w(mut self, max_width: Pixels) -> Self {
|
||||||
self.max_width = Some(max_width);
|
self.max_width = Some(max_width);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the overlay of the modal, defaults to `true`.
|
/// Set the overlay of the dialog, defaults to `true`.
|
||||||
pub fn overlay(mut self, overlay: bool) -> Self {
|
pub fn overlay(mut self, overlay: bool) -> Self {
|
||||||
self.overlay = overlay;
|
self.overlay = overlay;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the overlay closable of the modal, defaults to `true`.
|
/// Set the overlay closable of the dialog, defaults to `true`.
|
||||||
///
|
///
|
||||||
/// When the overlay is clicked, the modal will be closed.
|
/// When the overlay is clicked, the dialog will be closed.
|
||||||
pub fn overlay_closable(mut self, overlay_closable: bool) -> Self {
|
pub fn overlay_closable(mut self, overlay_closable: bool) -> Self {
|
||||||
self.overlay_closable = overlay_closable;
|
self.overlay_closable = overlay_closable;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set whether to support keyboard esc to close the modal, defaults to `true`.
|
/// Set whether to support keyboard esc to close the dialog, defaults to `true`.
|
||||||
pub fn keyboard(mut self, keyboard: bool) -> Self {
|
pub fn keyboard(mut self, keyboard: bool) -> Self {
|
||||||
self.keyboard = keyboard;
|
self.keyboard = keyboard;
|
||||||
self
|
self
|
||||||
@@ -251,22 +250,22 @@ impl Modal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParentElement for Modal {
|
impl ParentElement for Dialog {
|
||||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||||
self.content.extend(elements);
|
self.content.extend(elements);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Styled for Modal {
|
impl Styled for Dialog {
|
||||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||||
&mut self.style
|
&mut self.style
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderOnce for Modal {
|
impl RenderOnce for Dialog {
|
||||||
fn render(self, window: &mut Window, cx: &mut App) -> impl gpui::IntoElement {
|
fn render(self, window: &mut Window, cx: &mut App) -> impl gpui::IntoElement {
|
||||||
let layer_ix = self.layer_ix;
|
let layer_ix = self.layer_ix;
|
||||||
let is_topmost = layer_ix + 1 == Root::read(window, cx).active_modals.len();
|
let is_topmost = layer_ix + 1 == Root::read(window, cx).active_dialogs.len();
|
||||||
let has_footer = self.footer.is_some();
|
let has_footer = self.footer.is_some();
|
||||||
let on_close = self.on_close.clone();
|
let on_close = self.on_close.clone();
|
||||||
let on_ok = self.on_ok.clone();
|
let on_ok = self.on_ok.clone();
|
||||||
@@ -296,7 +295,7 @@ impl RenderOnce for Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_close(&ClickEvent::default(), window, cx);
|
on_close(&ClickEvent::default(), window, cx);
|
||||||
window.close_modal(cx);
|
window.close_dialog(cx);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
@@ -327,7 +326,7 @@ impl RenderOnce for Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
on_close(&ClickEvent::default(), window, cx);
|
on_close(&ClickEvent::default(), window, cx);
|
||||||
window.close_modal(cx);
|
window.close_dialog(cx);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
@@ -344,10 +343,7 @@ impl RenderOnce for Modal {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let offset_top = px(layer_ix as f32 * 16.);
|
let offset_top = px(layer_ix as f32 * 16.);
|
||||||
let y = self.margin_top.unwrap_or(view_size.height / 10.) + offset_top;
|
let top_offset = self.margin_top.unwrap_or(px(0.)) + offset_top;
|
||||||
let x = view_size.width / 2. - self.width / 2.;
|
|
||||||
let card_top = window_paddings.top + y;
|
|
||||||
let card_left = window_paddings.left + x;
|
|
||||||
|
|
||||||
let mut padding_right = px(16.);
|
let mut padding_right = px(16.);
|
||||||
let mut padding_left = px(16.);
|
let mut padding_left = px(16.);
|
||||||
@@ -386,11 +382,9 @@ impl RenderOnce for Modal {
|
|||||||
.min_h_24()
|
.min_h_24()
|
||||||
.refine_style(&self.style)
|
.refine_style(&self.style)
|
||||||
// There style is high priority, can't be overridden.
|
// There style is high priority, can't be overridden.
|
||||||
.absolute()
|
|
||||||
.occlude()
|
.occlude()
|
||||||
.relative()
|
.relative()
|
||||||
.left(card_left)
|
.top(top_offset)
|
||||||
.top(card_top)
|
|
||||||
.w(self.width)
|
.w(self.width)
|
||||||
.when_some(self.max_width, |this, w| this.max_w(w))
|
.when_some(self.max_width, |this, w| this.max_w(w))
|
||||||
.child(
|
.child(
|
||||||
@@ -430,7 +424,7 @@ impl RenderOnce for Modal {
|
|||||||
.on_click(move |_, window, cx| {
|
.on_click(move |_, window, cx| {
|
||||||
on_cancel(&ClickEvent::default(), window, cx);
|
on_cancel(&ClickEvent::default(), window, cx);
|
||||||
on_close(&ClickEvent::default(), window, cx);
|
on_close(&ClickEvent::default(), window, cx);
|
||||||
window.close_modal(cx);
|
window.close_dialog(cx);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -466,7 +460,8 @@ impl RenderOnce for Modal {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.with_animation("slide-down", animation, move |this, delta| {
|
.with_animation("slide-down", animation, move |this, delta| {
|
||||||
let y_offset = px(0.) + delta * px(30.);
|
// Settle at the target offset; only the entrance is animated.
|
||||||
|
let y_offset = px(30.) * (delta - 1.);
|
||||||
// This is equivalent to `shadow_xl` with an extra opacity.
|
// This is equivalent to `shadow_xl` with an extra opacity.
|
||||||
let shadow = vec![
|
let shadow = vec![
|
||||||
BoxShadow {
|
BoxShadow {
|
||||||
@@ -484,10 +479,10 @@ impl RenderOnce for Modal {
|
|||||||
inset: false,
|
inset: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
this.top(card_top + y_offset).shadow(shadow)
|
this.top(top_offset + y_offset).shadow(shadow)
|
||||||
});
|
});
|
||||||
|
|
||||||
Dialog::new(cx)
|
gpui_base::Dialog::new(cx)
|
||||||
.layer(layer_ix, is_topmost)
|
.layer(layer_ix, is_topmost)
|
||||||
.focus_handle(self.focus_handle.clone())
|
.focus_handle(self.focus_handle.clone())
|
||||||
.close_on_escape(self.keyboard)
|
.close_on_escape(self.keyboard)
|
||||||
@@ -499,7 +494,7 @@ impl RenderOnce for Modal {
|
|||||||
.on_cancel(move |event, window, cx| on_cancel(event, window, cx))
|
.on_cancel(move |event, window, cx| on_cancel(event, window, cx))
|
||||||
.on_close(move |event, window, cx| {
|
.on_close(move |event, window, cx| {
|
||||||
on_close(event, window, cx);
|
on_close(event, window, cx);
|
||||||
window.close_modal(cx);
|
window.close_dialog(cx);
|
||||||
})
|
})
|
||||||
.backdrop(backdrop)
|
.backdrop(backdrop)
|
||||||
.popup(card)
|
.popup(card)
|
||||||
@@ -16,7 +16,7 @@ use gpui_base::dock::{
|
|||||||
DockAreaRenderer, DockContext, DragPanel, DropIndicator, InsertTarget, NodeId, PaneNode,
|
DockAreaRenderer, DockContext, DragPanel, DropIndicator, InsertTarget, NodeId, PaneNode,
|
||||||
PaneRef, PanelId, TabGroupContext, TabGroupRenderer,
|
PaneRef, PanelId, TabGroupContext, TabGroupRenderer,
|
||||||
};
|
};
|
||||||
use gpui_base::{Placement, ResizeHandleContext, Side};
|
use gpui_base::{HandleEdge, Placement, ResizeHandleContext};
|
||||||
use theme::{ActiveTheme, TABBAR_HEIGHT};
|
use theme::{ActiveTheme, TABBAR_HEIGHT};
|
||||||
|
|
||||||
use crate::button::{Button, ButtonVariants as _};
|
use crate::button::{Button, ButtonVariants as _};
|
||||||
@@ -225,12 +225,14 @@ impl DockSkin {
|
|||||||
DockPlacement::Center => "dock-resize-handle-center",
|
DockPlacement::Center => "dock-resize-handle-center",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let edge = match placement {
|
||||||
|
DockPlacement::Left => Some(HandleEdge::Trailing),
|
||||||
|
DockPlacement::Right | DockPlacement::Bottom => Some(HandleEdge::Leading),
|
||||||
|
DockPlacement::Center => None,
|
||||||
|
};
|
||||||
|
|
||||||
resize_handle(id, placement.axis())
|
resize_handle(id, placement.axis())
|
||||||
.placement(if placement.is_left() {
|
.when_some(edge, |this, edge| this.inside(edge))
|
||||||
Side::Left
|
|
||||||
} else {
|
|
||||||
Side::Right
|
|
||||||
})
|
|
||||||
.with_appearance(resize_handle_appearance())
|
.with_appearance(resize_handle_appearance())
|
||||||
.on_drag(DockResizeHandle, move |info, _, _, cx| {
|
.on_drag(DockResizeHandle, move |info, _, _, cx| {
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub use crate::Disableable;
|
|||||||
pub mod animation;
|
pub mod animation;
|
||||||
pub mod avatar;
|
pub mod avatar;
|
||||||
pub mod button;
|
pub mod button;
|
||||||
|
pub mod dialog;
|
||||||
pub mod divider;
|
pub mod divider;
|
||||||
pub mod dock;
|
pub mod dock;
|
||||||
pub mod group_box;
|
pub mod group_box;
|
||||||
@@ -19,7 +20,6 @@ pub mod input;
|
|||||||
pub mod markdown;
|
pub mod markdown;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod modal;
|
|
||||||
pub mod nav;
|
pub mod nav;
|
||||||
pub mod nav_item;
|
pub mod nav_item;
|
||||||
pub mod notification;
|
pub mod notification;
|
||||||
|
|||||||
+43
-43
@@ -13,23 +13,23 @@ use theme::{
|
|||||||
CLIENT_SIDE_DECORATION_SHADOW,
|
CLIENT_SIDE_DECORATION_SHADOW,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::modal::Modal;
|
use crate::dialog::Dialog;
|
||||||
use crate::notification::{Notification, NotificationList};
|
use crate::notification::{Notification, NotificationList};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub struct ActiveModal {
|
pub struct ActiveDialog {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
/// The previous focused handle before opening the modal.
|
/// The previous focused handle before opening the dialog.
|
||||||
previous_focused_handle: Option<WeakFocusHandle>,
|
previous_focused_handle: Option<WeakFocusHandle>,
|
||||||
builder: Rc<dyn Fn(Modal, &mut Window, &mut App) -> Modal + 'static>,
|
builder: Rc<dyn Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveModal {
|
impl ActiveDialog {
|
||||||
fn new(
|
fn new(
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
previous_focused_handle: Option<WeakFocusHandle>,
|
previous_focused_handle: Option<WeakFocusHandle>,
|
||||||
builder: impl Fn(Modal, &mut Window, &mut App) -> Modal + 'static,
|
builder: impl Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
focus_handle,
|
focus_handle,
|
||||||
@@ -41,10 +41,10 @@ impl ActiveModal {
|
|||||||
|
|
||||||
/// Root is a view for the App window for as the top level view (Must be the first view in the window).
|
/// Root is a view for the App window for as the top level view (Must be the first view in the window).
|
||||||
///
|
///
|
||||||
/// It is used to manage the Modal, and Notification.
|
/// It is used to manage the Dialog, and Notification.
|
||||||
pub struct Root {
|
pub struct Root {
|
||||||
/// All active models
|
/// All active dialogs
|
||||||
pub(crate) active_modals: Vec<ActiveModal>,
|
pub(crate) active_dialogs: Vec<ActiveDialog>,
|
||||||
|
|
||||||
/// Notification layer
|
/// Notification layer
|
||||||
pub(crate) notification: Entity<NotificationList>,
|
pub(crate) notification: Entity<NotificationList>,
|
||||||
@@ -56,7 +56,7 @@ pub struct Root {
|
|||||||
impl Root {
|
impl Root {
|
||||||
pub fn new(view: AnyView, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
pub fn new(view: AnyView, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
active_modals: Vec::new(),
|
active_dialogs: Vec::new(),
|
||||||
notification: cx.new(|cx| NotificationList::new(window, cx)),
|
notification: cx.new(|cx| NotificationList::new(window, cx)),
|
||||||
view,
|
view,
|
||||||
}
|
}
|
||||||
@@ -98,63 +98,63 @@ impl Root {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the modal layer.
|
/// Render the dialog layer.
|
||||||
pub fn render_modal_layer(
|
pub fn render_dialog_layer(
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<impl IntoElement + use<>> {
|
) -> Option<impl IntoElement + use<>> {
|
||||||
let root = window.root::<Root>()??;
|
let root = window.root::<Root>()??;
|
||||||
let active_modals = root.read(cx).active_modals.clone();
|
let active_dialogs = root.read(cx).active_dialogs.clone();
|
||||||
|
|
||||||
if active_modals.is_empty() {
|
if active_dialogs.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut show_overlay_ix = None;
|
let mut show_overlay_ix = None;
|
||||||
|
|
||||||
let mut modals = active_modals
|
let mut dialogs = active_dialogs
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, active_modal)| {
|
.map(|(i, active_dialog)| {
|
||||||
let mut modal = Modal::new(window, cx);
|
let mut dialog = Dialog::new(window, cx);
|
||||||
|
|
||||||
modal = (active_modal.builder)(modal, window, cx);
|
dialog = (active_dialog.builder)(dialog, window, cx);
|
||||||
|
|
||||||
// Give the modal the focus handle, because `modal` is a temporary value, is not possible to
|
// Give the dialog the focus handle, because `dialog` is a temporary value, is not possible to
|
||||||
// keep the focus handle in the modal.
|
// keep the focus handle in the dialog.
|
||||||
//
|
//
|
||||||
// So we keep the focus handle in the `active_modal`, this is owned by the `Root`.
|
// So we keep the focus handle in the `active_dialog`, this is owned by the `Root`.
|
||||||
modal.focus_handle = active_modal.focus_handle.clone();
|
dialog.focus_handle = active_dialog.focus_handle.clone();
|
||||||
|
|
||||||
modal.layer_ix = i;
|
dialog.layer_ix = i;
|
||||||
// Find the modal which one needs to show overlay.
|
// Find the dialog which one needs to show overlay.
|
||||||
if modal.has_overlay() {
|
if dialog.has_overlay() {
|
||||||
show_overlay_ix = Some(i);
|
show_overlay_ix = Some(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
modal
|
dialog
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if let Some(ix) = show_overlay_ix
|
if let Some(ix) = show_overlay_ix
|
||||||
&& let Some(modal) = modals.get_mut(ix)
|
&& let Some(dialog) = dialogs.get_mut(ix)
|
||||||
{
|
{
|
||||||
modal.overlay_visible = true;
|
dialog.overlay_visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(div().children(modals))
|
Some(div().children(dialogs))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open a modal.
|
/// Open a dialog.
|
||||||
pub fn open_modal<F>(&mut self, builder: F, window: &mut Window, cx: &mut Context<'_, Self>)
|
pub fn open_dialog<F>(&mut self, builder: F, window: &mut Window, cx: &mut Context<'_, Self>)
|
||||||
where
|
where
|
||||||
F: Fn(Modal, &mut Window, &mut App) -> Modal + 'static,
|
F: Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static,
|
||||||
{
|
{
|
||||||
let previous_focused_handle = window.focused(cx).map(|h| h.downgrade());
|
let previous_focused_handle = window.focused(cx).map(|h| h.downgrade());
|
||||||
let focus_handle = cx.focus_handle();
|
let focus_handle = cx.focus_handle();
|
||||||
focus_handle.focus(window, cx);
|
focus_handle.focus(window, cx);
|
||||||
|
|
||||||
self.active_modals.push(ActiveModal::new(
|
self.active_dialogs.push(ActiveDialog::new(
|
||||||
focus_handle,
|
focus_handle,
|
||||||
previous_focused_handle,
|
previous_focused_handle,
|
||||||
builder,
|
builder,
|
||||||
@@ -163,10 +163,10 @@ impl Root {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close the topmost modal.
|
/// Close the topmost dialog.
|
||||||
pub fn close_modal(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
pub fn close_dialog(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
if let Some(handle) = self
|
if let Some(handle) = self
|
||||||
.active_modals
|
.active_dialogs
|
||||||
.pop()
|
.pop()
|
||||||
.and_then(|d| d.previous_focused_handle)
|
.and_then(|d| d.previous_focused_handle)
|
||||||
.and_then(|h| h.upgrade())
|
.and_then(|h| h.upgrade())
|
||||||
@@ -177,12 +177,12 @@ impl Root {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close all modals.
|
/// Close all dialogs.
|
||||||
pub fn close_all_modals(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
pub fn close_all_dialogs(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.active_modals.clear();
|
self.active_dialogs.clear();
|
||||||
|
|
||||||
let previous_focused_handle = self
|
let previous_focused_handle = self
|
||||||
.active_modals
|
.active_dialogs
|
||||||
.first()
|
.first()
|
||||||
.and_then(|d| d.previous_focused_handle.clone());
|
.and_then(|d| d.previous_focused_handle.clone());
|
||||||
|
|
||||||
@@ -193,9 +193,9 @@ impl Root {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if there are any active modals.
|
/// Check if there are any active dialogs.
|
||||||
pub fn has_active_modals(&self) -> bool {
|
pub fn has_active_dialogs(&self) -> bool {
|
||||||
!self.active_modals.is_empty()
|
!self.active_dialogs.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a notification to the notification layer.
|
/// Push a notification to the notification layer.
|
||||||
|
|||||||
+20
-20
@@ -3,24 +3,24 @@ use std::rc::Rc;
|
|||||||
use gpui::{App, ElementId, Entity, Window};
|
use gpui::{App, ElementId, Entity, Window};
|
||||||
|
|
||||||
use crate::Root;
|
use crate::Root;
|
||||||
use crate::modal::Modal;
|
use crate::dialog::Dialog;
|
||||||
use crate::notification::Notification;
|
use crate::notification::Notification;
|
||||||
|
|
||||||
/// Extension trait for [`Window`] to add modal, notification .. functionality.
|
/// Extension trait for [`Window`] to add dialog, notification .. functionality.
|
||||||
pub trait WindowExtension: Sized {
|
pub trait WindowExtension: Sized {
|
||||||
/// Opens a Modal.
|
/// Opens a Dialog.
|
||||||
fn open_modal<F>(&mut self, cx: &mut App, builder: F)
|
fn open_dialog<F>(&mut self, cx: &mut App, builder: F)
|
||||||
where
|
where
|
||||||
F: Fn(Modal, &mut Window, &mut App) -> Modal + 'static;
|
F: Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static;
|
||||||
|
|
||||||
/// Return true, if there is an active Modal.
|
/// Return true, if there is an active Dialog.
|
||||||
fn has_active_modal(&mut self, cx: &mut App) -> bool;
|
fn has_active_dialog(&mut self, cx: &mut App) -> bool;
|
||||||
|
|
||||||
/// Closes the last active Modal.
|
/// Closes the last active Dialog.
|
||||||
fn close_modal(&mut self, cx: &mut App);
|
fn close_dialog(&mut self, cx: &mut App);
|
||||||
|
|
||||||
/// Closes all active Modals.
|
/// Closes all active Dialogs.
|
||||||
fn close_all_modals(&mut self, cx: &mut App);
|
fn close_all_dialogs(&mut self, cx: &mut App);
|
||||||
|
|
||||||
/// Returns number of notifications.
|
/// Returns number of notifications.
|
||||||
fn notifications(&mut self, cx: &mut App) -> Rc<Vec<Entity<Notification>>>;
|
fn notifications(&mut self, cx: &mut App) -> Rc<Vec<Entity<Notification>>>;
|
||||||
@@ -46,31 +46,31 @@ pub trait WindowExtension: Sized {
|
|||||||
|
|
||||||
impl WindowExtension for Window {
|
impl WindowExtension for Window {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn open_modal<F>(&mut self, cx: &mut App, builder: F)
|
fn open_dialog<F>(&mut self, cx: &mut App, builder: F)
|
||||||
where
|
where
|
||||||
F: Fn(Modal, &mut Window, &mut App) -> Modal + 'static,
|
F: Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static,
|
||||||
{
|
{
|
||||||
Root::update(self, cx, move |root, window, cx| {
|
Root::update(self, cx, move |root, window, cx| {
|
||||||
root.open_modal(builder, window, cx);
|
root.open_dialog(builder, window, cx);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn has_active_modal(&mut self, cx: &mut App) -> bool {
|
fn has_active_dialog(&mut self, cx: &mut App) -> bool {
|
||||||
Root::read(self, cx).has_active_modals()
|
Root::read(self, cx).has_active_dialogs()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn close_modal(&mut self, cx: &mut App) {
|
fn close_dialog(&mut self, cx: &mut App) {
|
||||||
Root::update(self, cx, move |root, window, cx| {
|
Root::update(self, cx, move |root, window, cx| {
|
||||||
root.close_modal(window, cx);
|
root.close_dialog(window, cx);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn close_all_modals(&mut self, cx: &mut App) {
|
fn close_all_dialogs(&mut self, cx: &mut App) {
|
||||||
Root::update(self, cx, |root, window, cx| {
|
Root::update(self, cx, |root, window, cx| {
|
||||||
root.close_all_modals(window, cx);
|
root.close_all_dialogs(window, cx);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use ui::{Disableable, StyledExt, WindowExtension, divider, v_flex};
|
|||||||
pub fn open(window: &mut Window, cx: &mut App) {
|
pub fn open(window: &mut Window, cx: &mut App) {
|
||||||
let import = cx.new(|cx| ImportIdentity::new(window, cx));
|
let import = cx.new(|cx| ImportIdentity::new(window, cx));
|
||||||
|
|
||||||
window.open_modal(cx, move |this, _window, _cx| {
|
window.open_dialog(cx, move |this, _window, _cx| {
|
||||||
this.width(px(450.))
|
this.width(px(450.))
|
||||||
.show_close(false)
|
.show_close(false)
|
||||||
.overlay_closable(false)
|
.overlay_closable(false)
|
||||||
@@ -138,7 +138,7 @@ impl ImportIdentity {
|
|||||||
Ok(keys) => {
|
Ok(keys) => {
|
||||||
nostr.update_in(cx, |this, window, cx| {
|
nostr.update_in(cx, |this, window, cx| {
|
||||||
this.set_signer(keys, cx);
|
this.set_signer(keys, cx);
|
||||||
window.close_modal(cx);
|
window.close_dialog(cx);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@@ -5,21 +5,22 @@ use anyhow::Error;
|
|||||||
use chat::{ChatRegistry, Room, RoomKind};
|
use chat::{ChatRegistry, Room, RoomKind};
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString,
|
AnyElement, App, AppContext, Context, Entity, InteractiveElement, IntoElement, ParentElement,
|
||||||
Styled, Subscription, Task, WeakEntity, Window, div, px, uniform_list,
|
Render, SharedString, StatefulInteractiveElement as _, Styled, Subscription, Task, WeakEntity,
|
||||||
|
Window, div, px, uniform_list,
|
||||||
};
|
};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::PersonRegistry;
|
||||||
|
use settings::AppSettings;
|
||||||
use state::NostrRegistry;
|
use state::NostrRegistry;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
use ui::avatar::Avatar;
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants};
|
||||||
|
use ui::dialog::DialogButtonProps;
|
||||||
use ui::dock::{DockArea, DockPlacement, PanelHandle};
|
use ui::dock::{DockArea, DockPlacement, PanelHandle};
|
||||||
use ui::input::{Input, InputEvent, InputState};
|
use ui::input::{Input, InputEvent, InputState};
|
||||||
use ui::modal::ModalButtonProps;
|
|
||||||
use ui::nav::Nav;
|
use ui::nav::Nav;
|
||||||
use ui::{Disableable, IconName, Selectable, Sizable, WindowExtension, h_flex, v_flex};
|
use ui::{Disableable, IconName, Sizable, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
use crate::sidebar::nav_avatar;
|
|
||||||
|
|
||||||
/// Maximum number of members a chat can have.
|
/// Maximum number of members a chat can have.
|
||||||
const MAX_MEMBERS: usize = 3;
|
const MAX_MEMBERS: usize = 3;
|
||||||
@@ -27,14 +28,14 @@ const MAX_MEMBERS: usize = 3;
|
|||||||
pub fn open(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut App) {
|
pub fn open(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut App) {
|
||||||
let view = cx.new(|cx| NewChat::new(dock, window, cx));
|
let view = cx.new(|cx| NewChat::new(dock, window, cx));
|
||||||
|
|
||||||
window.open_modal(cx, {
|
window.open_dialog(cx, {
|
||||||
let view = view.clone();
|
let view = view.clone();
|
||||||
move |this, _window, _cx| {
|
move |this, _window, _cx| {
|
||||||
this.width(px(420.))
|
this.width(px(420.))
|
||||||
.title("New chat")
|
.title("New chat")
|
||||||
.confirm()
|
.confirm()
|
||||||
.button_props(
|
.button_props(
|
||||||
ModalButtonProps::default()
|
DialogButtonProps::default()
|
||||||
.cancel_text("Cancel")
|
.cancel_text("Cancel")
|
||||||
.ok_text("Start"),
|
.ok_text("Start"),
|
||||||
)
|
)
|
||||||
@@ -341,18 +342,20 @@ impl NewChat {
|
|||||||
.flatten()
|
.flatten()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(offset, public_key)| {
|
.map(|(offset, public_key)| {
|
||||||
let selected = self.members.contains(public_key);
|
|
||||||
let profile = persons.read(cx).get(public_key, cx);
|
let profile = persons.read(cx).get(public_key, cx);
|
||||||
let public_key = *public_key;
|
let public_key = *public_key;
|
||||||
|
|
||||||
Nav::new(SharedString::from(format!("contact-{}", start + offset)))
|
Nav::new(SharedString::from(format!("contact-{}", start + offset)))
|
||||||
.label(profile.name())
|
.label(profile.name())
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.when_some(
|
.when(!AppSettings::get_hide_avatar(cx), |this| {
|
||||||
nav_avatar(Some(profile.avatar_seed()), profile.avatar(), cx),
|
this.prefix(
|
||||||
|this, avatar| this.prefix(avatar),
|
Avatar::from_source(profile.avatar())
|
||||||
)
|
.seed(profile.avatar_seed())
|
||||||
.selected(selected)
|
.small()
|
||||||
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.on_click(cx.listener(move |this, _event, _window, cx| {
|
.on_click(cx.listener(move |this, _event, _window, cx| {
|
||||||
this.toggle_member(&public_key, cx);
|
this.toggle_member(&public_key, cx);
|
||||||
}))
|
}))
|
||||||
@@ -360,6 +363,38 @@ impl NewChat {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_members(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let persons = PersonRegistry::global(cx);
|
||||||
|
|
||||||
|
h_flex()
|
||||||
|
.flex_wrap()
|
||||||
|
.gap_1()
|
||||||
|
.children(self.members.iter().map(|public_key| {
|
||||||
|
let profile = persons.read(cx).get(public_key, cx);
|
||||||
|
let public_key = *public_key;
|
||||||
|
let id = SharedString::from(format!("m-{}", public_key.to_hex()));
|
||||||
|
|
||||||
|
h_flex()
|
||||||
|
.id(id)
|
||||||
|
.gap_1()
|
||||||
|
.p_0p5()
|
||||||
|
.rounded_full()
|
||||||
|
.bg(cx.theme().ghost_element_background_alt)
|
||||||
|
.text_size(px(10.))
|
||||||
|
.hover(|this| this.bg(cx.theme().ghost_element_hover))
|
||||||
|
.child(
|
||||||
|
Avatar::from_source(profile.avatar())
|
||||||
|
.seed(profile.avatar_seed())
|
||||||
|
.xsmall()
|
||||||
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
|
.child(div().max_w_32().truncate().child(profile.name()))
|
||||||
|
.on_click(cx.listener(move |this, _event, _window, cx| {
|
||||||
|
this.toggle_member(&public_key, cx);
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for NewChat {
|
impl Render for NewChat {
|
||||||
@@ -371,43 +406,42 @@ impl Render for NewChat {
|
|||||||
v_flex()
|
v_flex()
|
||||||
.gap_3()
|
.gap_3()
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
|
.w_full()
|
||||||
|
.child(Input::new(&self.input).flex_1().text_sm().cleanable(true))
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
Button::new("add")
|
||||||
.gap_1()
|
.icon(IconName::Plus)
|
||||||
.w_full()
|
.tooltip("Add person")
|
||||||
.child(Input::new(&self.input).flex_1().text_sm().cleanable(true))
|
.ghost()
|
||||||
.child(
|
.large()
|
||||||
Button::new("add")
|
.disabled(self.resolving)
|
||||||
.icon(IconName::Plus)
|
.on_click(cx.listener(|this, _event, window, cx| {
|
||||||
.tooltip("Add person")
|
this.submit(window, cx);
|
||||||
.ghost()
|
})),
|
||||||
.large()
|
),
|
||||||
.disabled(self.resolving)
|
|
||||||
.on_click(cx.listener(|this, _event, window, cx| {
|
|
||||||
this.submit(window, cx);
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map(|this| {
|
|
||||||
if let Some(error) = self.error.clone() {
|
|
||||||
this.child(
|
|
||||||
div()
|
|
||||||
.text_xs()
|
|
||||||
.text_center()
|
|
||||||
.text_color(cx.theme().text_danger)
|
|
||||||
.child(error),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
this.child(
|
|
||||||
div()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(cx.theme().text_placeholder)
|
|
||||||
.child(notice),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
.when(!self.members.is_empty(), |this| {
|
||||||
|
this.child(self.render_members(cx))
|
||||||
|
})
|
||||||
|
.map(|this| {
|
||||||
|
if let Some(error) = self.error.clone() {
|
||||||
|
this.child(
|
||||||
|
div()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(cx.theme().text_danger)
|
||||||
|
.child(error),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.child(
|
||||||
|
div()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(cx.theme().text_placeholder)
|
||||||
|
.child(notice),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
uniform_list(
|
uniform_list(
|
||||||
"contacts",
|
"contacts",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use ui::input::{Input, InputState};
|
|||||||
pub fn open(window: &mut Window, cx: &mut App) {
|
pub fn open(window: &mut Window, cx: &mut App) {
|
||||||
let name_input = cx.new(|cx| InputState::new(window, cx).placeholder("Community name"));
|
let name_input = cx.new(|cx| InputState::new(window, cx).placeholder("Community name"));
|
||||||
|
|
||||||
window.open_modal(cx, move |this, _window, _cx| {
|
window.open_dialog(cx, move |this, _window, _cx| {
|
||||||
let name_input = name_input.clone();
|
let name_input = name_input.clone();
|
||||||
|
|
||||||
this.width(px(380.))
|
this.width(px(380.))
|
||||||
@@ -14,6 +14,7 @@ pub fn open(window: &mut Window, cx: &mut App) {
|
|||||||
.title("New community")
|
.title("New community")
|
||||||
.child(Input::new(&name_input))
|
.child(Input::new(&name_input))
|
||||||
.on_ok(move |_event, _window, cx| {
|
.on_ok(move |_event, _window, cx| {
|
||||||
|
let community = CommunityRegistry::global(cx);
|
||||||
let name = name_input.read(cx).value().trim().to_owned();
|
let name = name_input.read(cx).value().trim().to_owned();
|
||||||
|
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
@@ -25,8 +26,9 @@ pub fn open(window: &mut Window, cx: &mut App) {
|
|||||||
..CommunityMetadata::default()
|
..CommunityMetadata::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
CommunityRegistry::global(cx)
|
community.update(cx, |registry, cx| {
|
||||||
.update(cx, |registry, cx| registry.create(metadata, cx));
|
registry.create(metadata, cx);
|
||||||
|
});
|
||||||
|
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ impl RestoreEncryption {
|
|||||||
this.set_announcement(Keys::new(secret), cx);
|
this.set_announcement(Keys::new(secret), cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close the current modal
|
// Close the current dialog
|
||||||
window.close_modal(cx);
|
window.close_dialog(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_error<S>(&mut self, message: S, cx: &mut Context<Self>)
|
fn set_error<S>(&mut self, message: S, cx: &mut Context<Self>)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ impl Screening {
|
|||||||
|
|
||||||
subscriptions.push(cx.on_release_in(window, move |this, window, cx| {
|
subscriptions.push(cx.on_release_in(window, move |this, window, cx| {
|
||||||
this.tasks.clear();
|
this.tasks.clear();
|
||||||
window.close_all_modals(cx);
|
window.close_all_dialogs(cx);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
cx.defer_in(window, |this, _window, cx| {
|
cx.defer_in(window, |this, _window, cx| {
|
||||||
@@ -259,7 +259,7 @@ impl Screening {
|
|||||||
self.tasks.push(cx.spawn_in(window, async move |_, cx| {
|
self.tasks.push(cx.spawn_in(window, async move |_, cx| {
|
||||||
if task.await.is_ok() {
|
if task.await.is_ok() {
|
||||||
cx.update(|window, cx| {
|
cx.update(|window, cx| {
|
||||||
window.close_modal(cx);
|
window.close_dialog(cx);
|
||||||
window.push_notification("Report submitted successfully", cx);
|
window.push_notification("Report submitted successfully", cx);
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
@@ -270,7 +270,7 @@ impl Screening {
|
|||||||
fn mutual_contacts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn mutual_contacts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let contacts = self.mutual_contacts.clone();
|
let contacts = self.mutual_contacts.clone();
|
||||||
|
|
||||||
window.open_modal(cx, move |this, _window, _cx| {
|
window.open_dialog(cx, move |this, _window, _cx| {
|
||||||
let contacts = contacts.clone();
|
let contacts = contacts.clone();
|
||||||
let total = contacts.len();
|
let total = contacts.len();
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ impl Workspace {
|
|||||||
// Subscribe to the nostr events
|
// Subscribe to the nostr events
|
||||||
cx.subscribe_in(&nostr, window, move |_this, _state, event, window, cx| {
|
cx.subscribe_in(&nostr, window, move |_this, _state, event, window, cx| {
|
||||||
if let StateEvent::SignerChanged = event {
|
if let StateEvent::SignerChanged = event {
|
||||||
window.close_all_modals(cx);
|
window.close_all_dialogs(cx);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -242,7 +242,7 @@ impl Workspace {
|
|||||||
Command::ShowSettings => {
|
Command::ShowSettings => {
|
||||||
let view = settings::init(window, cx);
|
let view = settings::init(window, cx);
|
||||||
|
|
||||||
window.open_modal(cx, move |this, _window, _cx| {
|
window.open_dialog(cx, move |this, _window, _cx| {
|
||||||
this.width(px(520.))
|
this.width(px(520.))
|
||||||
.show_close(true)
|
.show_close(true)
|
||||||
.pb_2()
|
.pb_2()
|
||||||
@@ -387,7 +387,7 @@ impl Workspace {
|
|||||||
let device = DeviceRegistry::global(cx);
|
let device = DeviceRegistry::global(cx);
|
||||||
let ent = device.downgrade();
|
let ent = device.downgrade();
|
||||||
|
|
||||||
window.open_modal(cx, move |this, _window, cx| {
|
window.open_dialog(cx, move |this, _window, cx| {
|
||||||
let ent = ent.clone();
|
let ent = ent.clone();
|
||||||
|
|
||||||
this.confirm()
|
this.confirm()
|
||||||
@@ -410,7 +410,7 @@ impl Workspace {
|
|||||||
this.set_announcement(Keys::generate(), cx);
|
this.set_announcement(Keys::generate(), cx);
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
// true to close modal
|
// true to close dialog
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -418,7 +418,7 @@ impl Workspace {
|
|||||||
|
|
||||||
fn import_encryption(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn import_encryption(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let restore = cx.new(|cx| RestoreEncryption::new(window, cx));
|
let restore = cx.new(|cx| RestoreEncryption::new(window, cx));
|
||||||
window.open_modal(cx, move |this, _window, _cx| {
|
window.open_dialog(cx, move |this, _window, _cx| {
|
||||||
this.width(px(420.))
|
this.width(px(420.))
|
||||||
.title("Restore Encryption")
|
.title("Restore Encryption")
|
||||||
.child(restore.clone())
|
.child(restore.clone())
|
||||||
@@ -426,7 +426,7 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn theme_selector(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn theme_selector(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
window.open_modal(cx, move |this, _window, cx| {
|
window.open_dialog(cx, move |this, _window, cx| {
|
||||||
let registry = ThemeRegistry::global(cx);
|
let registry = ThemeRegistry::global(cx);
|
||||||
let themes = registry.read(cx).themes();
|
let themes = registry.read(cx).themes();
|
||||||
|
|
||||||
@@ -508,7 +508,7 @@ impl Workspace {
|
|||||||
|
|
||||||
impl Render for Workspace {
|
impl Render for Workspace {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let modal_layer = Root::render_modal_layer(window, cx);
|
let dialog_layer = Root::render_dialog_layer(window, cx);
|
||||||
let notification_layer = Root::render_notification_layer(window, cx);
|
let notification_layer = Root::render_notification_layer(window, cx);
|
||||||
|
|
||||||
div()
|
div()
|
||||||
@@ -519,7 +519,7 @@ impl Render for Workspace {
|
|||||||
.child(self.dock.clone())
|
.child(self.dock.clone())
|
||||||
// Notifications
|
// Notifications
|
||||||
.children(notification_layer)
|
.children(notification_layer)
|
||||||
// Modals
|
// Dialogs
|
||||||
.children(modal_layer)
|
.children(dialog_layer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ use gpui::{
|
|||||||
use instant::Duration;
|
use instant::Duration;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::PersonRegistry;
|
||||||
|
use settings::AppSettings;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use state::{FIND_DELAY, NostrRegistry};
|
use state::{FIND_DELAY, NostrRegistry};
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
use ui::avatar::Avatar;
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants};
|
||||||
use ui::dock::{DockArea, DockPlacement, Panel, PanelEvent, PanelHandle};
|
use ui::dock::{DockArea, DockPlacement, Panel, PanelEvent, PanelHandle};
|
||||||
use ui::input::{Input, InputEvent, InputState};
|
use ui::input::{Input, InputEvent, InputState};
|
||||||
@@ -23,8 +25,6 @@ use ui::nav::Nav;
|
|||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
use ui::{Icon, IconName, Selectable, Sizable, StyledExt, WindowExtension, h_flex, v_flex};
|
use ui::{Icon, IconName, Selectable, Sizable, StyledExt, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
use crate::sidebar::nav_avatar;
|
|
||||||
|
|
||||||
const INPUT_PLACEHOLDER: &str = "Find or start a conversation";
|
const INPUT_PLACEHOLDER: &str = "Find or start a conversation";
|
||||||
|
|
||||||
pub fn init(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut App) -> Entity<SearchPanel> {
|
pub fn init(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut App) -> Entity<SearchPanel> {
|
||||||
@@ -337,6 +337,7 @@ impl SearchPanel {
|
|||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
cx: &Context<Self>,
|
cx: &Context<Self>,
|
||||||
) -> Vec<impl IntoElement + use<>> {
|
) -> Vec<impl IntoElement + use<>> {
|
||||||
|
let hide_avatar = AppSettings::get_hide_avatar(cx);
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
|
|
||||||
// Get the results
|
// Get the results
|
||||||
@@ -365,10 +366,14 @@ impl SearchPanel {
|
|||||||
.label(profile.name())
|
.label(profile.name())
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.font_medium()
|
.font_medium()
|
||||||
.when_some(
|
.when(!hide_avatar, |this| {
|
||||||
nav_avatar(Some(profile.avatar_seed()), profile.avatar(), cx),
|
this.prefix(
|
||||||
|this, avatar| this.prefix(avatar),
|
Avatar::from_source(profile.avatar())
|
||||||
)
|
.seed(profile.avatar_seed())
|
||||||
|
.small()
|
||||||
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.on_click(handler)
|
.on_click(handler)
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
@@ -382,6 +387,7 @@ impl SearchPanel {
|
|||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
cx: &Context<Self>,
|
cx: &Context<Self>,
|
||||||
) -> Vec<impl IntoElement + use<>> {
|
) -> Vec<impl IntoElement + use<>> {
|
||||||
|
let hide_avatar = AppSettings::get_hide_avatar(cx);
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
|
|
||||||
// Get the contact list
|
// Get the contact list
|
||||||
@@ -410,10 +416,14 @@ impl SearchPanel {
|
|||||||
.label(profile.name().trim())
|
.label(profile.name().trim())
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.font_medium()
|
.font_medium()
|
||||||
.when_some(
|
.when(!hide_avatar, |this| {
|
||||||
nav_avatar(Some(profile.avatar_seed()), profile.avatar(), cx),
|
this.prefix(
|
||||||
|this, avatar| this.prefix(avatar),
|
Avatar::from_source(profile.avatar())
|
||||||
)
|
.seed(profile.avatar_seed())
|
||||||
|
.small()
|
||||||
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.on_click(handler)
|
.on_click(handler)
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ use state::{NostrRegistry, StateEvent};
|
|||||||
use theme::{ActiveTheme, TABBAR_HEIGHT};
|
use theme::{ActiveTheme, TABBAR_HEIGHT};
|
||||||
use ui::avatar::Avatar;
|
use ui::avatar::Avatar;
|
||||||
use ui::button::{Button, ButtonCustomVariant, ButtonVariants};
|
use ui::button::{Button, ButtonCustomVariant, ButtonVariants};
|
||||||
|
use ui::dialog::DialogButtonProps;
|
||||||
use ui::dock::{ClosePanel, DockArea, DockPlacement, Panel, PanelEvent, PanelHandle};
|
use ui::dock::{ClosePanel, DockArea, DockPlacement, Panel, PanelEvent, PanelHandle};
|
||||||
use ui::indicator::Indicator;
|
use ui::indicator::Indicator;
|
||||||
use ui::menu::{DropdownMenu, PopupMenuItem};
|
use ui::menu::{DropdownMenu, PopupMenuItem};
|
||||||
use ui::modal::ModalButtonProps;
|
|
||||||
use ui::nav::Nav;
|
use ui::nav::Nav;
|
||||||
use ui::nav_item::NavItem;
|
use ui::nav_item::NavItem;
|
||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
@@ -44,7 +44,7 @@ mod tab;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use tab::SidebarTab;
|
use tab::SidebarTab;
|
||||||
pub(crate) use utils::{nav_avatar, nav_icon, pick_banner};
|
pub(crate) use utils::{nav_icon, pick_banner};
|
||||||
|
|
||||||
pub enum SidebarRow {
|
pub enum SidebarRow {
|
||||||
Room { room: Entity<Room> },
|
Room { room: Entity<Room> },
|
||||||
@@ -833,6 +833,8 @@ impl Sidebar {
|
|||||||
rows: &[SidebarRow],
|
rows: &[SidebarRow],
|
||||||
cx: &Context<Sidebar>,
|
cx: &Context<Sidebar>,
|
||||||
) -> Vec<AnyElement> {
|
) -> Vec<AnyElement> {
|
||||||
|
let hide_avatar = AppSettings::get_hide_avatar(cx);
|
||||||
|
|
||||||
rows.get(range.clone())
|
rows.get(range.clone())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
@@ -855,8 +857,13 @@ impl Sidebar {
|
|||||||
.label(name)
|
.label(name)
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.font_medium()
|
.font_medium()
|
||||||
.when_some(nav_avatar(Some(seed), picture, cx), |this, avatar| {
|
.when(!hide_avatar, |this| {
|
||||||
this.prefix(avatar)
|
this.prefix(
|
||||||
|
Avatar::from_source(picture)
|
||||||
|
.seed(seed)
|
||||||
|
.small()
|
||||||
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.suffix(
|
.suffix(
|
||||||
div()
|
div()
|
||||||
@@ -876,11 +883,11 @@ impl Sidebar {
|
|||||||
if kind != RoomKind::Ongoing && AppSettings::get_screening(cx) {
|
if kind != RoomKind::Ongoing && AppSettings::get_screening(cx) {
|
||||||
let screening = screening::init(peer, window, cx);
|
let screening = screening::init(peer, window, cx);
|
||||||
|
|
||||||
window.open_modal(cx, move |this, _window, _cx| {
|
window.open_dialog(cx, move |this, _window, _cx| {
|
||||||
this.confirm()
|
this.confirm()
|
||||||
.child(screening.clone())
|
.child(screening.clone())
|
||||||
.button_props(
|
.button_props(
|
||||||
ModalButtonProps::default()
|
DialogButtonProps::default()
|
||||||
.cancel_text("Ignore")
|
.cancel_text("Ignore")
|
||||||
.ok_text("Response"),
|
.ok_text("Response"),
|
||||||
)
|
)
|
||||||
@@ -904,10 +911,14 @@ impl Sidebar {
|
|||||||
.label(person.name())
|
.label(person.name())
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.font_medium()
|
.font_medium()
|
||||||
.when_some(
|
.when(!hide_avatar, |this| {
|
||||||
nav_avatar(Some(person.avatar_seed()), person.avatar(), cx),
|
this.prefix(
|
||||||
|this, avatar| this.prefix(avatar),
|
Avatar::from_source(person.avatar())
|
||||||
)
|
.seed(person.avatar_seed())
|
||||||
|
.small()
|
||||||
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.on_click(move |_event, _window, cx| {
|
.on_click(move |_event, _window, cx| {
|
||||||
if let Err(error) = sidebar.update(cx, |this, cx| {
|
if let Err(error) = sidebar.update(cx, |this, cx| {
|
||||||
@@ -929,8 +940,13 @@ impl Sidebar {
|
|||||||
Nav::new(SharedString::from(format!("com-{index}")))
|
Nav::new(SharedString::from(format!("com-{index}")))
|
||||||
.label(name)
|
.label(name)
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.when_some(nav_avatar(Some(seed), picture, cx), |this, avatar| {
|
.when(!hide_avatar, |this| {
|
||||||
this.prefix(avatar)
|
this.prefix(
|
||||||
|
Avatar::from_source(picture)
|
||||||
|
.seed(seed)
|
||||||
|
.small()
|
||||||
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.on_click(move |_event, window, cx| {
|
.on_click(move |_event, window, cx| {
|
||||||
ui::dock::add_panel_to(
|
ui::dock::add_panel_to(
|
||||||
@@ -1021,6 +1037,7 @@ impl Sidebar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn member_row(&self, public_key: &PublicKey, cx: &App) -> AnyElement {
|
fn member_row(&self, public_key: &PublicKey, cx: &App) -> AnyElement {
|
||||||
|
let hide_avatar = AppSettings::get_hide_avatar(cx);
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
let person = persons.read(cx).get(public_key, cx);
|
let person = persons.read(cx).get(public_key, cx);
|
||||||
|
|
||||||
@@ -1028,10 +1045,14 @@ impl Sidebar {
|
|||||||
.label(person.name())
|
.label(person.name())
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.font_medium()
|
.font_medium()
|
||||||
.when_some(
|
.when(!hide_avatar, |this| {
|
||||||
nav_avatar(Some(person.avatar_seed()), person.avatar(), cx),
|
this.prefix(
|
||||||
|this, avatar| this.prefix(avatar),
|
Avatar::from_source(person.avatar())
|
||||||
)
|
.seed(person.avatar_seed())
|
||||||
|
.small()
|
||||||
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,9 @@
|
|||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use gpui::prelude::FluentBuilder as _;
|
use gpui::{AnyElement, App, IntoElement, ParentElement, SharedString, Styled, px};
|
||||||
use gpui::{AnyElement, App, ImageSource, IntoElement, ParentElement, SharedString, Styled, px};
|
|
||||||
use settings::AppSettings;
|
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::avatar::{Avatar, PixelAvatar};
|
|
||||||
use ui::{Icon, IconName, Sizable, h_flex};
|
use ui::{Icon, IconName, Sizable, h_flex};
|
||||||
|
|
||||||
pub(crate) fn nav_avatar(
|
|
||||||
seed: Option<impl Into<SharedString>>,
|
|
||||||
picture: Option<impl Into<ImageSource>>,
|
|
||||||
cx: &App,
|
|
||||||
) -> Option<AnyElement> {
|
|
||||||
if AppSettings::get_hide_avatar(cx) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match (seed.map(Into::into), picture.map(Into::into)) {
|
|
||||||
(None, None) => None,
|
|
||||||
(seed, Some(picture)) => Some(
|
|
||||||
Avatar::from_source(picture)
|
|
||||||
.when_some(seed, |avatar, seed| avatar.seed(seed))
|
|
||||||
.small()
|
|
||||||
.flex_shrink_0()
|
|
||||||
.into_any_element(),
|
|
||||||
),
|
|
||||||
(Some(seed), None) => Some(
|
|
||||||
PixelAvatar::new(seed)
|
|
||||||
.small()
|
|
||||||
.flex_shrink_0()
|
|
||||||
.into_any_element(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn nav_icon(icon: IconName, cx: &App) -> AnyElement {
|
pub(crate) fn nav_icon(icon: IconName, cx: &App) -> AnyElement {
|
||||||
h_flex()
|
h_flex()
|
||||||
.flex_shrink_0()
|
.flex_shrink_0()
|
||||||
|
|||||||
Reference in New Issue
Block a user