Detailed changes
@@ -10,7 +10,7 @@ dependencies = [
"auto_update",
"editor",
"extension_host",
- "futures 0.3.30",
+ "futures 0.3.31",
"gpui",
"language",
"lsp",
@@ -23,19 +23,13 @@ dependencies = [
[[package]]
name = "addr2line"
-version = "0.24.1"
+version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
- "gimli 0.31.0",
+ "gimli 0.31.1",
]
-[[package]]
-name = "adler"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-
[[package]]
name = "adler2"
version = "2.0.0"
@@ -100,8 +94,8 @@ dependencies = [
"miow",
"parking_lot",
"piper",
- "polling 3.7.3",
- "regex-automata 0.4.7",
+ "polling 3.7.4",
+ "regex-automata 0.4.9",
"rustix-openpty",
"serde",
"signal-hook",
@@ -124,9 +118,9 @@ checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
[[package]]
name = "allocator-api2"
-version = "0.2.18"
+version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9"
[[package]]
name = "alsa"
@@ -192,9 +186,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
-version = "0.6.15"
+version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -207,36 +201,36 @@ dependencies = [
[[package]]
name = "anstyle"
-version = "1.0.8"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
-version = "0.2.5"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.1.1"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.4"
+version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
+checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -245,13 +239,13 @@ version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
- "futures 0.3.30",
+ "futures 0.3.31",
"http_client",
"schemars",
"serde",
"serde_json",
"strum 0.25.0",
- "thiserror",
+ "thiserror 1.0.69",
"util",
]
@@ -278,9 +272,9 @@ dependencies = [
[[package]]
name = "arbitrary"
-version = "1.3.2"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
[[package]]
name = "arg_enum_proc_macro"
@@ -301,9 +295,9 @@ checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
[[package]]
name = "arrayref"
-version = "0.3.8"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
@@ -396,7 +390,7 @@ dependencies = [
"env_logger 0.11.5",
"feature_flags",
"fs",
- "futures 0.3.30",
+ "futures 0.3.31",
"fuzzy",
"globset",
"gpui",
@@ -463,7 +457,7 @@ dependencies = [
"collections",
"derive_more",
"extension",
- "futures 0.3.30",
+ "futures 0.3.31",
"gpui",
"language",
"language_model",
@@ -573,14 +567,14 @@ dependencies = [
[[package]]
name = "async-executor"
-version = "1.13.0"
+version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
+checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
dependencies = [
"async-task",
"concurrent-queue",
- "fastrand 2.1.1",
- "futures-lite 2.3.0",
+ "fastrand 2.2.0",
+ "futures-lite 2.5.0",
"slab",
]
@@ -604,7 +598,7 @@ checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a"
dependencies = [
"async-lock 3.4.0",
"blocking",
- "futures-lite 2.3.0",
+ "futures-lite 2.5.0",
]
[[package]]
@@ -615,10 +609,10 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
dependencies = [
"async-channel 2.3.1",
"async-executor",
- "async-io 2.3.4",
+ "async-io 2.4.0",
"async-lock 3.4.0",
"blocking",
- "futures-lite 2.3.0",
+ "futures-lite 2.5.0",
"once_cell",
]
@@ -644,18 +638,18 @@ dependencies = [
[[package]]
name = "async-io"
-version = "2.3.4"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8"
+checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
dependencies = [
"async-lock 3.4.0",
"cfg-if",
"concurrent-queue",
"futures-io",
- "futures-lite 2.3.0",
+ "futures-lite 2.5.0",
"parking",
- "polling 3.7.3",
- "rustix 0.38.35",
+ "polling 3.7.4",
+ "rustix 0.38.40",
"slab",
"tracing",
"windows-sys 0.59.0",
@@ -689,7 +683,7 @@ checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec"
dependencies = [
"futures-util",
"native-tls",
- "thiserror",
+ "thiserror 1.0.69",
"url",
]
@@ -710,9 +704,9 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
dependencies = [
- "async-io 2.3.4",
+ "async-io 2.4.0",
"blocking",
- "futures-lite 2.3.0",
+ "futures-lite 2.5.0",
]
[[package]]
@@ -720,7 +714,7 @@ name = "async-pipe"
version = "0.1.3"
source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553"
dependencies = [
- "futures 0.3.30",
+ "futures 0.3.31",
"log",
]
@@ -737,28 +731,27 @@ dependencies = [
"cfg-if",
"event-listener 3.1.0",
"futures-lite 1.13.0",
- "rustix 0.38.35",
+ "rustix 0.38.40",
"windows-sys 0.48.0",
]
[[package]]
name = "async-process"
-version = "2.2.4"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8a07789659a4d385b79b18b9127fc27e1a59e1e89117c78c5ea3b806f016374"
+checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
dependencies = [
"async-channel 2.3.1",
- "async-io 2.3.4",
+ "async-io 2.4.0",
"async-lock 3.4.0",
"async-signal",
"async-task",
"blocking",
"cfg-if",
"event-listener 5.3.1",
- "futures-lite 2.3.0",
- "rustix 0.38.35",
+ "futures-lite 2.5.0",
+ "rustix 0.38.40",
"tracing",
- "windows-sys 0.59.0",
]
[[package]]
@@ -789,13 +782,13 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
dependencies = [
- "async-io 2.3.4",
+ "async-io 2.4.0",
"async-lock 3.4.0",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
- "rustix 0.38.35",
+ "rustix 0.38.40",
"signal-hook-registry",
"slab",
"windows-sys 0.59.0",
@@ -803,21 +796,21 @@ dependencies = [
[[package]]
name = "async-std"
-version = "1.12.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
+checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615"
dependencies = [
"async-attributes",
"async-channel 1.9.0",
"async-global-executor",
- "async-io 1.13.0",
- "async-lock 2.8.0",
- "async-process 1.8.1",
+ "async-io 2.4.0",
+ "async-lock 3.4.0",
+ "async-process 2.3.0",
"crossbeam-utils",
"futures-channel",
"futures-core",
"futures-io",
- "futures-lite 1.13.0",
+ "futures-lite 2.5.0",
"gloo-timers",
"kv-log-macro",
"log",
@@ -831,9 +824,9 @@ dependencies = [
[[package]]
name = "async-stream"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
dependencies = [
"async-stream-impl",
"futures-core",
@@ -842,9 +835,9 @@ dependencies = [
[[package]]
name = "async-stream-impl"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
@@ -867,7 +860,7 @@ dependencies = [
"serde_qs 0.10.1",
"smart-default",
"smol_str",
- "thiserror",
+ "thiserror 1.0.69",
"tokio",
]
@@ -963,9 +956,9 @@ checksum = "00b9f7252833d5ed4b00aa9604b563529dd5e11de9c23615de2dcdf91eb87b52"
dependencies = [
"async-compression",
"crc32fast",
- "futures-lite 2.3.0",
+ "futures-lite 2.5.0",
"pin-project",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -974,7 +967,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233"
dependencies = [
- "bytes 1.7.2",
+ "bytes 1.8.0",
"futures-sink",
"futures-util",
"memchr",
@@ -1044,9 +1037,9 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "av1-grain"
@@ -1064,18 +1057,18 @@ dependencies = [
[[package]]
name = "avif-serialize"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2"
+checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62"
dependencies = [
"arrayvec",
]
[[package]]
name = "aws-config"
-version = "1.5.5"
+version = "1.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697"
+checksum = "9b49afaa341e8dd8577e1a2200468f98956d6eda50bcf4a53246cc00174ba924"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -1089,8 +1082,8 @@ dependencies = [
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
- "bytes 1.7.2",
- "fastrand 2.1.1",
+ "bytes 1.8.0",
+ "fastrand 2.2.0",
"hex",
"http 0.2.12",
"ring",
@@ -1128,8 +1121,8 @@ dependencies = [
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
- "bytes 1.7.2",
- "fastrand 2.1.1",
+ "bytes 1.8.0",
+ "fastrand 2.2.0",
"http 0.2.12",
"http-body 0.4.6",
"once_cell",
@@ -1154,7 +1147,7 @@ dependencies = [
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"http 0.2.12",
"once_cell",
"regex-lite",
@@ -1163,11 +1156,10 @@ dependencies = [
[[package]]
name = "aws-sdk-s3"
-version = "1.47.0"
+version = "1.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cca49303c05d2a740b8a4552fac63a4db6ead84f7e7eeed04761fd3014c26f25"
+checksum = "0e531658a0397d22365dfe26c3e1c0c8448bf6a3a2d8a098ded802f2b1261615"
dependencies = [
- "ahash 0.8.11",
"aws-credential-types",
"aws-runtime",
"aws-sigv4",
@@ -1181,8 +1173,8 @@ dependencies = [
"aws-smithy-types",
"aws-smithy-xml",
"aws-types",
- "bytes 1.7.2",
- "fastrand 2.1.1",
+ "bytes 1.8.0",
+ "fastrand 2.2.0",
"hex",
"hmac",
"http 0.2.12",
@@ -1198,9 +1190,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
-version = "1.40.0"
+version = "1.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5879bec6e74b648ce12f6085e7245417bc5f6d672781028384d2e494be3eb6d"
+checksum = "09677244a9da92172c8dc60109b4a9658597d4d298b188dd0018b6a66b410ca4"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -1211,7 +1203,7 @@ dependencies = [
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"http 0.2.12",
"once_cell",
"regex-lite",
@@ -1220,9 +1212,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
-version = "1.41.0"
+version = "1.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ef4cd9362f638c22a3b959fd8df292e7e47fdf170270f86246b97109b5f2f7d"
+checksum = "81fea2f3a8bb3bd10932ae7ad59cc59f65f270fc9183a7e91f501dc5efbef7ee"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -1233,7 +1225,7 @@ dependencies = [
"aws-smithy-runtime-api",
"aws-smithy-types",
"aws-types",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"http 0.2.12",
"once_cell",
"regex-lite",
@@ -1242,9 +1234,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
-version = "1.40.0"
+version = "1.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b1e2735d2ab28b35ecbb5496c9d41857f52a0d6a0075bbf6a8af306045ea6f6"
+checksum = "6ada54e5f26ac246dc79727def52f7f8ed38915cb47781e2a72213957dc3a7d5"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -1274,7 +1266,7 @@ dependencies = [
"aws-smithy-http",
"aws-smithy-runtime-api",
"aws-smithy-types",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"crypto-bigint 0.5.5",
"form_urlencoded",
"hex",
@@ -1305,13 +1297,13 @@ dependencies = [
[[package]]
name = "aws-smithy-checksums"
-version = "0.60.12"
+version = "0.60.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "598b1689d001c4d4dc3cb386adb07d37786783aee3ac4b324bcadac116bf3d23"
+checksum = "ba1a71073fca26775c8b5189175ea8863afb1c9ea2cceb02a5de5ad9dfbaa795"
dependencies = [
"aws-smithy-http",
"aws-smithy-types",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"crc32c",
"crc32fast",
"hex",
@@ -1331,7 +1323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cef7d0a272725f87e51ba2bf89f8c21e4df61b9e49ae1ac367a6d69916ef7c90"
dependencies = [
"aws-smithy-types",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"crc32fast",
]
@@ -1344,7 +1336,7 @@ dependencies = [
"aws-smithy-eventstream",
"aws-smithy-runtime-api",
"aws-smithy-types",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"bytes-utils",
"futures-core",
"http 0.2.12",
@@ -1385,8 +1377,8 @@ dependencies = [
"aws-smithy-http",
"aws-smithy-runtime-api",
"aws-smithy-types",
- "bytes 1.7.2",
- "fastrand 2.1.1",
+ "bytes 1.8.0",
+ "fastrand 2.2.0",
"h2 0.3.26",
"http 0.2.12",
"http-body 0.4.6",
@@ -1410,7 +1402,7 @@ checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd"
dependencies = [
"aws-smithy-async",
"aws-smithy-types",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"http 0.2.12",
"http 1.1.0",
"pin-project-lite",
@@ -1426,7 +1418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fbd94a32b3a7d55d3806fe27d98d3ad393050439dd05eb53ece36ec5e3d3510"
dependencies = [
"base64-simd",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"bytes-utils",
"futures-core",
"http 0.2.12",
@@ -1447,9 +1439,9 @@ dependencies = [
[[package]]
name = "aws-smithy-xml"
-version = "0.60.8"
+version = "0.60.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55"
+checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc"
dependencies = [
"xmlparser",
]
@@ -1478,7 +1470,7 @@ dependencies = [
"axum-core",
"base64 0.21.7",
"bitflags 1.3.2",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"futures-util",
"headers",
"http 0.2.12",
@@ -1511,7 +1503,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
dependencies = [
"async-trait",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"futures-util",
"http 0.2.12",
"http-body 0.4.6",
@@ -1528,7 +1520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a320103719de37b7b4da4c8eb629d4573f6bcfd3dfe80d3208806895ccf81d"
dependencies = [
"axum",
- "bytes 1.7.2",
+ "bytes 1.8.0",
"futures-util",
"http 0.2.12",
"mime",
@@ -1551,7 +1543,7 @@ dependencies = [
"addr2line",
"cfg-if",
"libc",
- "miniz_oxide 0.8.0",
+ "miniz_oxide",
"object",
"rustc-demangle",
"windows-targets 0.52.6",
@@ -1599,9 +1591,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bigdecimal"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee"
+checksum = "8f850665a0385e070b64c38d2354e6c104c8479c59868d1e48a0c13ee2c7a1c1"
dependencies = [
"autocfg",
"libm",
@@ -1620,26 +1612,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "bindgen"
-version = "0.69.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
-dependencies = [
- "bitflags 2.6.0",
- "cexpr",
- "clang-sys",
- "itertools 0.12.1",
- "lazy_static",
- "lazycell",
- "proc-macro2",
- "quote",
- "regex",
- "rustc-hash 1.1.0",
- "shlex",
- "syn 2.0.87",
-]
-
[[package]]
name = "bindgen"
version = "0.70.1"
@@ -1728,9 +1700,9 @@ dependencies = [
[[package]]
name = "bitstream-io"
-version = "2.5.3"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452"
+checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
[[package]]
name = "bitvec"
@@ -1841,15 +1813,15 @@ dependencies = [
"async-channel 2.3.1",
"async-task",
"futures-io",
- "futures-lite 2.3.0",
+ "futures-lite 2.5.0",
"piper",
]
[[package]]
name = "borsh"
-version = "1.5.1"
+version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed"
+checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03"
dependencies = [
"borsh-derive",
"cfg_aliases 0.2.1",
@@ -1857,16 +1829,15 @@ dependencies = [
[[package]]
name = "borsh-derive"
-version = "1.5.1"
+version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b"
+checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244"
dependencies = [
"once_cell",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.87",
- "syn_derive",
]
[[package]]
@@ -1884,20 +1855,20 @@ dependencies = [
[[package]]
name = "bstr"
-version = "1.10.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
+checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22"
dependencies = [
"memchr",
- "regex-automata 0.4.7",
+ "regex-automata 0.4.9",
"serde",
]
[[package]]
name = "built"
-version = "0.7.4"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4"
+checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b"
[[package]]
name = "bumpalo"
@@ -1935,18 +1906,18 @@ dependencies = [
[[package]]
name = "bytemuck"
-version = "1.17.1"
+version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2"
+checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
-version = "1.7.1"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
+checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
dependencies = [
"proc-macro2",
"quote",
@@ -1977,9 +1948,9 @@ dependencies = [
[[package]]
name = "bytes"
-version = "1.7.2"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
+checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
[[package]]
name = "bytes-utils"
@@ -1987,7 +1958,7 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35"
dependencies = [
- "bytes 1.7.2",
+ "bytes 1.8.0",
"either",
]
@@ -2022,7 +1993,7 @@ dependencies = [
"collections",
"feature_flags",
"fs",
- "futures 0.3.30",
+ "futures 0.3.31",
"gpui",
"http_client",
"language",
@@ -2045,10 +2016,10 @@ checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
dependencies = [
"bitflags 2.6.0",
"log",
- "polling 3.7.3",
- "rustix 0.38.35",
+ "polling 3.7.4",
+ "rustix 0.38.40",
"slab",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -2058,7 +2029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
dependencies = [
"calloop",
- "rustix 0.38.35",
+ "rustix 0.38.40",
"wayland-backend",
"wayland-client",
]
@@ -2074,9 +2045,9 @@ dependencies = [
[[package]]
name = "cap-fs-ext"
-version = "3.2.0"
+version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb23061fc1c4ead4e45ca713080fe768e6234e959f5a5c399c39eb41aa34e56e"
+checksum = "e16619ada836f12897a72011fe99b03f0025b87a8dbbea4f3c9f89b458a23bf3"
dependencies = [
"cap-primitives",
"cap-std",
@@ -2086,21 +2057,21 @@ dependencies = [
[[package]]
name = "cap-net-ext"
-version = "3.2.0"
+version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f83ae11f116bcbafc5327c6af250341db96b5930046732e1905f7dc65887e0e1"
+checksum = "710b0eb776410a22c89a98f2f80b2187c2ac3a8206b99f3412332e63c9b09de0"
dependencies = [
"cap-primitives",
"cap-std",
- "rustix 0.38.35",
+ "rustix 0.38.40",
"smallvec",
]
[[package]]
name = "cap-primitives"
-version = "3.2.0"
+version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d00bd8d26c4270d950eaaa837387964a2089a1c3c349a690a1fa03221d29531"
+checksum = "82fa6c3f9773feab88d844aa50035a33fb6e7e7426105d2f4bb7aadc42a5f89a"
dependencies = [
"ambient-authority",
"fs-set-times",
@@ -2108,16 +2079,16 @@ dependencies = [
"io-lifetimes 2.0.3",
"ipnet",
"maybe-owned",
- "rustix 0.38.35",
+ "rustix 0.38.40",
"windows-sys 0.52.0",
"winx",
]
[[package]]
name = "cap-rand"
-version = "3.2.0"
+version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbcb16a619d8b8211ed61f42bd290d2a1ac71277a69cf8417ec0996fa92f5211"
+checksum = "53774d49369892b70184f8312e50c1b87edccb376691de4485b0ff554b27c36c"
dependencies = [
"ambient-authority",
"rand 0.8.5",
@@ -2125,27 +2096,27 @@ dependencies = [
[[package]]
name = "cap-std"
-version = "3.2.0"
+version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19eb8e3d71996828751c1ed3908a439639752ac6bdc874e41469ef7fc15fbd7f"
+checksum = "7f71b70818556b4fe2a10c7c30baac3f5f45e973f49fc2673d7c75c39d0baf5b"
dependencies = [
"cap-primitives",
"io-extras",
"io-lifetimes 2.0.3",
- "rustix 0.38.35",
+ "rustix 0.38.40",
]
[[package]]
name = "cap-time-ext"
-version = "3.2.0"
+version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61142dc51e25b7acc970ca578ce2c3695eac22bbba46c1073f5f583e78957725"
+checksum = "69dd48afa2363f746c93f961c211f6f099fb594a3446b8097bc5f79db51b6816"
dependencies = [
"ambient-authority",
"cap-primitives",
"iana-time-zone",
"once_cell",
- "rustix 0.38.35",
+ "rustix 0.38.40",
"winx",
]
@@ -2169,7 +2140,7 @@ dependencies = [
"semver",
"serde",
"serde_json",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -2204,7 +2175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb"
dependencies = [
"heck 0.4.1",
- "indexmap 2.4.0",
+ "indexmap 2.6.0",
"log",
"proc-macro2",
"quote",
@@ -2217,9 +2188,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.1.15"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
+checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
dependencies = [
"jobserver",
"libc",
@@ -2277,7 +2248,7 @@ dependencies = [
"client",
"clock",
"collections",
- "futures 0.3.30",
+ "futures 0.3.31",
"gpui",
"http_client",
"language",
@@ -2387,9 +2358,9 @@ dependencies = [
[[package]]
name = "clap_complete"
-version = "4.5.24"
+version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d7db6eca8c205649e8d3ccd05aa5042b1800a784e56bc7c43524fde8abbfa9b"
+checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01"
dependencies = [
"clap",
]
@@ -3333,7 +3333,8 @@ impl ContextEditor {
self.context.update(cx, |context, cx| {
for image in images {
- let Some(render_image) = image.to_image_data(cx).log_err() else {
+ let Some(render_image) = image.to_image_data(cx.svg_renderer()).log_err()
+ else {
continue;
};
let image_id = image.id();
@@ -1,6 +1,4 @@
-use gpui::{
- div, img, prelude::*, App, AppContext, ImageSource, Render, ViewContext, WindowOptions,
-};
+use gpui::{div, img, prelude::*, App, AppContext, Render, ViewContext, WindowOptions};
use std::path::PathBuf;
struct GifViewer {
@@ -16,7 +14,7 @@ impl GifViewer {
impl Render for GifViewer {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
div().size_full().child(
- img(ImageSource::File(self.gif_path.clone().into()))
+ img(self.gif_path.clone())
.size_full()
.object_fit(gpui::ObjectFit::Contain)
.id("gif"),
@@ -61,7 +61,7 @@ impl RenderOnce for ImageContainer {
}
struct ImageShowcase {
- local_resource: Arc<PathBuf>,
+ local_resource: Arc<std::path::Path>,
remote_resource: SharedUri,
asset_resource: SharedString,
}
@@ -153,9 +153,10 @@ fn main() {
cx.open_window(window_options, |cx| {
cx.new_view(|_cx| ImageShowcase {
// Relative path to your root project path
- local_resource: Arc::new(
- PathBuf::from_str("crates/gpui/examples/image/app-icon.png").unwrap(),
- ),
+ local_resource: PathBuf::from_str("crates/gpui/examples/image/app-icon.png")
+ .unwrap()
+ .into(),
+
remote_resource: "https://picsum.photos/512/512".into(),
asset_resource: "image/color.svg".into(),
@@ -0,0 +1,214 @@
+use std::{path::Path, sync::Arc, time::Duration};
+
+use anyhow::anyhow;
+use gpui::{
+ black, div, img, prelude::*, pulsating_between, px, red, size, Animation, AnimationExt, App,
+ AppContext, Asset, AssetLogger, AssetSource, Bounds, Hsla, ImageAssetLoader, ImageCacheError,
+ ImgResourceLoader, Length, Pixels, RenderImage, Resource, SharedString, ViewContext,
+ WindowBounds, WindowContext, WindowOptions, LOADING_DELAY,
+};
+
+struct Assets {}
+
+impl AssetSource for Assets {
+ fn load(&self, path: &str) -> anyhow::Result<Option<std::borrow::Cow<'static, [u8]>>> {
+ std::fs::read(path)
+ .map(Into::into)
+ .map_err(Into::into)
+ .map(Some)
+ }
+
+ fn list(&self, path: &str) -> anyhow::Result<Vec<SharedString>> {
+ Ok(std::fs::read_dir(path)?
+ .filter_map(|entry| {
+ Some(SharedString::from(
+ entry.ok()?.path().to_string_lossy().to_string(),
+ ))
+ })
+ .collect::<Vec<_>>())
+ }
+}
+
+const IMAGE: &str = "examples/image/app-icon.png";
+
+#[derive(Copy, Clone, Hash)]
+struct LoadImageParameters {
+ timeout: Duration,
+ fail: bool,
+}
+
+struct LoadImageWithParameters {}
+
+impl Asset for LoadImageWithParameters {
+ type Source = LoadImageParameters;
+
+ type Output = Result<Arc<RenderImage>, ImageCacheError>;
+
+ fn load(
+ parameters: Self::Source,
+ cx: &mut AppContext,
+ ) -> impl std::future::Future<Output = Self::Output> + Send + 'static {
+ let timer = cx.background_executor().timer(parameters.timeout);
+ let data = AssetLogger::<ImageAssetLoader>::load(
+ Resource::Path(Path::new(IMAGE).to_path_buf().into()),
+ cx,
+ );
+ async move {
+ timer.await;
+ if parameters.fail {
+ log::error!("Intentionally failed to load image");
+ Err(anyhow!("Failed to load image").into())
+ } else {
+ data.await
+ }
+ }
+ }
+}
+
+struct ImageLoadingExample {}
+
+impl ImageLoadingExample {
+ fn loading_element() -> impl IntoElement {
+ div().size_full().flex_none().p_0p5().rounded_sm().child(
+ div().size_full().with_animation(
+ "loading-bg",
+ Animation::new(Duration::from_secs(3))
+ .repeat()
+ .with_easing(pulsating_between(0.04, 0.24)),
+ move |this, delta| this.bg(black().opacity(delta)),
+ ),
+ )
+ }
+
+ fn fallback_element() -> impl IntoElement {
+ let fallback_color: Hsla = black().opacity(0.5);
+
+ div().size_full().flex_none().p_0p5().child(
+ div()
+ .size_full()
+ .flex()
+ .items_center()
+ .justify_center()
+ .rounded_sm()
+ .text_sm()
+ .text_color(fallback_color)
+ .border_1()
+ .border_color(fallback_color)
+ .child("?"),
+ )
+ }
+}
+
+impl Render for ImageLoadingExample {
+ fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+ div().flex().flex_col().size_full().justify_around().child(
+ div().flex().flex_row().w_full().justify_around().child(
+ div()
+ .flex()
+ .bg(gpui::white())
+ .size(Length::Definite(Pixels(300.0).into()))
+ .justify_center()
+ .items_center()
+ .child({
+ let image_source = LoadImageParameters {
+ timeout: LOADING_DELAY.saturating_sub(Duration::from_millis(25)),
+ fail: false,
+ };
+
+ // Load within the 'loading delay', should not show loading fallback
+ img(move |cx: &mut WindowContext| {
+ cx.use_asset::<LoadImageWithParameters>(&image_source)
+ })
+ .id("image-1")
+ .border_1()
+ .size_12()
+ .with_fallback(|| Self::fallback_element().into_any_element())
+ .border_color(red())
+ .with_loading(|| Self::loading_element().into_any_element())
+ .on_click(move |_, cx| {
+ cx.remove_asset::<LoadImageWithParameters>(&image_source);
+ })
+ })
+ .child({
+ // Load after a long delay
+ let image_source = LoadImageParameters {
+ timeout: Duration::from_secs(5),
+ fail: false,
+ };
+
+ img(move |cx: &mut WindowContext| {
+ cx.use_asset::<LoadImageWithParameters>(&image_source)
+ })
+ .id("image-2")
+ .with_fallback(|| Self::fallback_element().into_any_element())
+ .with_loading(|| Self::loading_element().into_any_element())
+ .size_12()
+ .border_1()
+ .border_color(red())
+ .on_click(move |_, cx| {
+ cx.remove_asset::<LoadImageWithParameters>(&image_source);
+ })
+ })
+ .child({
+ // Fail to load image after a long delay
+ let image_source = LoadImageParameters {
+ timeout: Duration::from_secs(5),
+ fail: true,
+ };
+
+ // Fail to load after a long delay
+ img(move |cx: &mut WindowContext| {
+ cx.use_asset::<LoadImageWithParameters>(&image_source)
+ })
+ .id("image-3")
+ .with_fallback(|| Self::fallback_element().into_any_element())
+ .with_loading(|| Self::loading_element().into_any_element())
+ .size_12()
+ .border_1()
+ .border_color(red())
+ .on_click(move |_, cx| {
+ cx.remove_asset::<LoadImageWithParameters>(&image_source);
+ })
+ })
+ .child({
+ // Ensure that the normal image loader doesn't spam logs
+ let image_source = Path::new(
+ "this/file/really/shouldn't/exist/or/won't/be/an/image/I/hope",
+ )
+ .to_path_buf();
+ img(image_source.clone())
+ .id("image-1")
+ .border_1()
+ .size_12()
+ .with_fallback(|| Self::fallback_element().into_any_element())
+ .border_color(red())
+ .with_loading(|| Self::loading_element().into_any_element())
+ .on_click(move |_, cx| {
+ cx.remove_asset::<ImgResourceLoader>(&image_source.clone().into());
+ })
+ }),
+ ),
+ )
+ }
+}
+
+fn main() {
+ env_logger::init();
+ App::new()
+ .with_assets(Assets {})
+ .run(|cx: &mut AppContext| {
+ let options = WindowOptions {
+ window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
+ None,
+ size(px(300.), Pixels(300.)),
+ cx,
+ ))),
+ ..Default::default()
+ };
+ cx.open_window(options, |cx| {
+ cx.activate(false);
+ cx.new_view(|_cx| ImageLoadingExample {})
+ })
+ .unwrap();
+ });
+}
@@ -747,7 +747,7 @@ impl AppContext {
}
/// Returns the SVG renderer GPUI uses
- pub(crate) fn svg_renderer(&self) -> SvgRenderer {
+ pub fn svg_renderer(&self) -> SvgRenderer {
self.svg_renderer.clone()
}
@@ -1369,7 +1369,7 @@ impl AppContext {
}
/// Remove an asset from GPUI's cache
- pub fn remove_cached_asset<A: Asset + 'static>(&mut self, source: &A::Source) {
+ pub fn remove_asset<A: Asset>(&mut self, source: &A::Source) {
let asset_id = (TypeId::of::<A>(), hash(source));
self.loading_assets.remove(&asset_id);
}
@@ -1378,12 +1378,7 @@ impl AppContext {
///
/// Note that the multiple calls to this method will only result in one `Asset::load` call at a
/// time, and the results of this call will be cached
- ///
- /// This asset will not be cached by default, see [Self::use_cached_asset]
- pub fn fetch_asset<A: Asset + 'static>(
- &mut self,
- source: &A::Source,
- ) -> (Shared<Task<A::Output>>, bool) {
+ pub fn fetch_asset<A: Asset>(&mut self, source: &A::Source) -> (Shared<Task<A::Output>>, bool) {
let asset_id = (TypeId::of::<A>(), hash(source));
let mut is_first = false;
let task = self
@@ -1,30 +1,43 @@
use crate::{AppContext, SharedString, SharedUri};
use futures::Future;
+
+use std::fmt::Debug;
use std::hash::{Hash, Hasher};
-use std::path::PathBuf;
+use std::marker::PhantomData;
+use std::path::{Path, PathBuf};
use std::sync::Arc;
+/// An enum representing
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
-pub(crate) enum UriOrPath {
+pub enum Resource {
+ /// This resource is at a given URI
Uri(SharedUri),
- Path(Arc<PathBuf>),
+ /// This resource is at a given path in the file system
+ Path(Arc<Path>),
+ /// This resource is embedded in the application binary
Embedded(SharedString),
}
-impl From<SharedUri> for UriOrPath {
+impl From<SharedUri> for Resource {
fn from(value: SharedUri) -> Self {
Self::Uri(value)
}
}
-impl From<Arc<PathBuf>> for UriOrPath {
- fn from(value: Arc<PathBuf>) -> Self {
+impl From<PathBuf> for Resource {
+ fn from(value: PathBuf) -> Self {
+ Self::Path(value.into())
+ }
+}
+
+impl From<Arc<Path>> for Resource {
+ fn from(value: Arc<Path>) -> Self {
Self::Path(value)
}
}
/// A trait for asynchronous asset loading.
-pub trait Asset {
+pub trait Asset: 'static {
/// The source of the asset.
type Source: Clone + Hash + Send;
@@ -38,6 +51,31 @@ pub trait Asset {
) -> impl Future<Output = Self::Output> + Send + 'static;
}
+/// An asset Loader that logs whatever passes through it
+pub enum AssetLogger<T> {
+ #[doc(hidden)]
+ _Phantom(PhantomData<T>, &'static dyn crate::seal::Sealed),
+}
+
+impl<R: Clone + Send, E: Clone + Send + std::error::Error, T: Asset<Output = Result<R, E>>> Asset
+ for AssetLogger<T>
+{
+ type Source = T::Source;
+
+ type Output = T::Output;
+
+ fn load(
+ source: Self::Source,
+ cx: &mut AppContext,
+ ) -> impl Future<Output = Self::Output> + Send + 'static {
+ let load = T::load(source, cx);
+ async {
+ load.await
+ .inspect_err(|e| log::error!("Failed to load asset: {}", e))
+ }
+ }
+}
+
/// Use a quick, non-cryptographically secure hash function to get an identifier from data
pub fn hash<T: Hash>(data: &T) -> u64 {
let mut hasher = collections::FxHasher::default();
@@ -2383,7 +2383,7 @@ where
/// A wrapper around an element that can store state, produced after assigning an ElementId.
pub struct Stateful<E> {
- element: E,
+ pub(crate) element: E,
}
impl<E> Styled for Stateful<E>
@@ -1,9 +1,11 @@
use crate::{
- px, AbsoluteLength, AppContext, Asset, Bounds, DefiniteLength, Element, ElementId,
- GlobalElementId, Hitbox, Image, InteractiveElement, Interactivity, IntoElement, LayoutId,
- Length, ObjectFit, Pixels, RenderImage, SharedString, SharedUri, StyleRefinement, Styled,
- SvgSize, UriOrPath, WindowContext,
+ px, AbsoluteLength, AnyElement, AppContext, Asset, AssetLogger, Bounds, DefiniteLength,
+ Element, ElementId, GlobalElementId, Hitbox, Image, InteractiveElement, Interactivity,
+ IntoElement, LayoutId, Length, ObjectFit, Pixels, RenderImage, Resource, SharedString,
+ SharedUri, StyleRefinement, Styled, SvgSize, Task, WindowContext,
};
+use anyhow::{anyhow, Result};
+
use futures::{AsyncReadExt, Future};
use image::{
codecs::gif::GifDecoder, AnimationDecoder, Frame, ImageBuffer, ImageError, ImageFormat,
@@ -11,45 +13,56 @@ use image::{
use smallvec::SmallVec;
use std::{
fs,
- io::Cursor,
- path::PathBuf,
+ io::{self, Cursor},
+ ops::{Deref, DerefMut},
+ path::{Path, PathBuf},
+ str::FromStr,
sync::Arc,
time::{Duration, Instant},
};
use thiserror::Error;
use util::ResultExt;
+use super::{FocusableElement, Stateful, StatefulInteractiveElement};
+
+/// The delay before showing the loading state.
+pub const LOADING_DELAY: Duration = Duration::from_millis(200);
+
+/// A type alias to the resource loader that the `img()` element uses.
+///
+/// Note: that this is only for Resources, like URLs or file paths.
+/// Custom loaders, or external images will not use this asset loader
+pub type ImgResourceLoader = AssetLogger<ImageAssetLoader>;
+
/// A source of image content.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone)]
pub enum ImageSource {
- /// Image content will be loaded from provided URI at render time.
- Uri(SharedUri),
- /// Image content will be loaded from the provided file at render time.
- File(Arc<PathBuf>),
+ /// The image content will be loaded from some resource location
+ Resource(Resource),
/// Cached image data
Render(Arc<RenderImage>),
/// Cached image data
Image(Arc<Image>),
- /// Image content will be loaded from Asset at render time.
- Embedded(SharedString),
+ /// A custom loading function to use
+ Custom(Arc<dyn Fn(&mut WindowContext) -> Option<Result<Arc<RenderImage>, ImageCacheError>>>),
}
fn is_uri(uri: &str) -> bool {
- uri.contains("://")
+ http_client::Uri::from_str(uri).is_ok()
}
impl From<SharedUri> for ImageSource {
fn from(value: SharedUri) -> Self {
- Self::Uri(value)
+ Self::Resource(Resource::Uri(value))
}
}
-impl From<&'static str> for ImageSource {
- fn from(s: &'static str) -> Self {
+impl<'a> From<&'a str> for ImageSource {
+ fn from(s: &'a str) -> Self {
if is_uri(s) {
- Self::Uri(s.into())
+ Self::Resource(Resource::Uri(s.to_string().into()))
} else {
- Self::Embedded(s.into())
+ Self::Resource(Resource::Embedded(s.to_string().into()))
}
}
}
@@ -57,32 +70,34 @@ impl From<&'static str> for ImageSource {
impl From<String> for ImageSource {
fn from(s: String) -> Self {
if is_uri(&s) {
- Self::Uri(s.into())
+ Self::Resource(Resource::Uri(s.into()))
} else {
- Self::Embedded(s.into())
+ Self::Resource(Resource::Embedded(s.into()))
}
}
}
impl From<SharedString> for ImageSource {
fn from(s: SharedString) -> Self {
- if is_uri(&s) {
- Self::Uri(s.into())
- } else {
- Self::Embedded(s)
- }
+ s.as_ref().into()
+ }
+}
+
+impl From<&Path> for ImageSource {
+ fn from(value: &Path) -> Self {
+ Self::Resource(value.to_path_buf().into())
}
}
-impl From<Arc<PathBuf>> for ImageSource {
- fn from(value: Arc<PathBuf>) -> Self {
- Self::File(value)
+impl From<Arc<Path>> for ImageSource {
+ fn from(value: Arc<Path>) -> Self {
+ Self::Resource(value.into())
}
}
impl From<PathBuf> for ImageSource {
fn from(value: PathBuf) -> Self {
- Self::File(value.into())
+ Self::Resource(value.into())
}
}
@@ -98,12 +113,80 @@ impl From<Arc<Image>> for ImageSource {
}
}
+impl<F: Fn(&mut WindowContext) -> Option<Result<Arc<RenderImage>, ImageCacheError>> + 'static>
+ From<F> for ImageSource
+{
+ fn from(value: F) -> Self {
+ Self::Custom(Arc::new(value))
+ }
+}
+
+/// The style of an image element.
+pub struct ImageStyle {
+ grayscale: bool,
+ object_fit: ObjectFit,
+ loading: Option<Box<dyn Fn() -> AnyElement>>,
+ fallback: Option<Box<dyn Fn() -> AnyElement>>,
+}
+
+impl Default for ImageStyle {
+ fn default() -> Self {
+ Self {
+ grayscale: false,
+ object_fit: ObjectFit::Contain,
+ loading: None,
+ fallback: None,
+ }
+ }
+}
+
+/// Style an image element.
+pub trait StyledImage: Sized {
+ /// Get a mutable [ImageStyle] from the element.
+ fn image_style(&mut self) -> &mut ImageStyle;
+
+ /// Set the image to be displayed in grayscale.
+ fn grayscale(mut self, grayscale: bool) -> Self {
+ self.image_style().grayscale = grayscale;
+ self
+ }
+
+ /// Set the object fit for the image.
+ fn object_fit(mut self, object_fit: ObjectFit) -> Self {
+ self.image_style().object_fit = object_fit;
+ self
+ }
+
+ /// Set the object fit for the image.
+ fn with_fallback(mut self, fallback: impl Fn() -> AnyElement + 'static) -> Self {
+ self.image_style().fallback = Some(Box::new(fallback));
+ self
+ }
+
+ /// Set the object fit for the image.
+ fn with_loading(mut self, loading: impl Fn() -> AnyElement + 'static) -> Self {
+ self.image_style().loading = Some(Box::new(loading));
+ self
+ }
+}
+
+impl StyledImage for Img {
+ fn image_style(&mut self) -> &mut ImageStyle {
+ &mut self.style
+ }
+}
+
+impl StyledImage for Stateful<Img> {
+ fn image_style(&mut self) -> &mut ImageStyle {
+ &mut self.element.style
+ }
+}
+
/// An image element.
pub struct Img {
interactivity: Interactivity,
source: ImageSource,
- grayscale: bool,
- object_fit: ObjectFit,
+ style: ImageStyle,
}
/// Create a new image element.
@@ -111,8 +194,7 @@ pub fn img(source: impl Into<ImageSource>) -> Img {
Img {
interactivity: Interactivity::default(),
source: source.into(),
- grayscale: false,
- object_fit: ObjectFit::Contain,
+ style: ImageStyle::default(),
}
}
@@ -125,16 +207,19 @@ impl Img {
"hdr", "exr", "pbm", "pam", "ppm", "pgm", "ff", "farbfeld", "qoi", "svg",
]
}
+}
- /// Set the image to be displayed in grayscale.
- pub fn grayscale(mut self, grayscale: bool) -> Self {
- self.grayscale = grayscale;
- self
+impl Deref for Stateful<Img> {
+ type Target = Img;
+
+ fn deref(&self) -> &Self::Target {
+ &self.element
}
- /// Set the object fit for the image.
- pub fn object_fit(mut self, object_fit: ObjectFit) -> Self {
- self.object_fit = object_fit;
- self
+}
+
+impl DerefMut for Stateful<Img> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.element
}
}
@@ -142,10 +227,17 @@ impl Img {
struct ImgState {
frame_index: usize,
last_frame_time: Option<Instant>,
+ started_loading: Option<(Instant, Task<()>)>,
+}
+
+/// The image layout state between frames
+pub struct ImgLayoutState {
+ frame_index: usize,
+ replacement: Option<AnyElement>,
}
impl Element for Img {
- type RequestLayoutState = usize;
+ type RequestLayoutState = ImgLayoutState;
type PrepaintState = Option<Hitbox>;
fn id(&self) -> Option<ElementId> {
@@ -157,11 +249,17 @@ impl Element for Img {
global_id: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (LayoutId, Self::RequestLayoutState) {
+ let mut layout_state = ImgLayoutState {
+ frame_index: 0,
+ replacement: None,
+ };
+
cx.with_optional_element_state(global_id, |state, cx| {
let mut state = state.map(|state| {
state.unwrap_or(ImgState {
frame_index: 0,
last_frame_time: None,
+ started_loading: None,
})
});
@@ -170,64 +268,105 @@ impl Element for Img {
let layout_id = self
.interactivity
.request_layout(global_id, cx, |mut style, cx| {
- if let Some(data) = self.source.use_data(cx) {
- if let Some(state) = &mut state {
- let frame_count = data.frame_count();
- if frame_count > 1 {
- let current_time = Instant::now();
- if let Some(last_frame_time) = state.last_frame_time {
- let elapsed = current_time - last_frame_time;
- let frame_duration =
- Duration::from(data.delay(state.frame_index));
-
- if elapsed >= frame_duration {
- state.frame_index = (state.frame_index + 1) % frame_count;
- state.last_frame_time =
- Some(current_time - (elapsed - frame_duration));
+ let mut replacement_id = None;
+
+ match self.source.use_data(cx) {
+ Some(Ok(data)) => {
+ if let Some(state) = &mut state {
+ let frame_count = data.frame_count();
+ if frame_count > 1 {
+ let current_time = Instant::now();
+ if let Some(last_frame_time) = state.last_frame_time {
+ let elapsed = current_time - last_frame_time;
+ let frame_duration =
+ Duration::from(data.delay(state.frame_index));
+
+ if elapsed >= frame_duration {
+ state.frame_index =
+ (state.frame_index + 1) % frame_count;
+ state.last_frame_time =
+ Some(current_time - (elapsed - frame_duration));
+ }
+ } else {
+ state.last_frame_time = Some(current_time);
}
- } else {
- state.last_frame_time = Some(current_time);
}
+ state.started_loading = None;
}
- }
- let image_size = data.size(frame_index);
-
- if let Length::Auto = style.size.width {
- style.size.width = match style.size.height {
- Length::Definite(DefiniteLength::Absolute(
- AbsoluteLength::Pixels(height),
- )) => Length::Definite(
- px(image_size.width.0 as f32 * height.0
- / image_size.height.0 as f32)
- .into(),
- ),
- _ => Length::Definite(px(image_size.width.0 as f32).into()),
- };
- }
+ let image_size = data.size(frame_index);
+
+ if let Length::Auto = style.size.width {
+ style.size.width = match style.size.height {
+ Length::Definite(DefiniteLength::Absolute(
+ AbsoluteLength::Pixels(height),
+ )) => Length::Definite(
+ px(image_size.width.0 as f32 * height.0
+ / image_size.height.0 as f32)
+ .into(),
+ ),
+ _ => Length::Definite(px(image_size.width.0 as f32).into()),
+ };
+ }
- if let Length::Auto = style.size.height {
- style.size.height = match style.size.width {
- Length::Definite(DefiniteLength::Absolute(
- AbsoluteLength::Pixels(width),
- )) => Length::Definite(
- px(image_size.height.0 as f32 * width.0
- / image_size.width.0 as f32)
- .into(),
- ),
- _ => Length::Definite(px(image_size.height.0 as f32).into()),
- };
- }
+ if let Length::Auto = style.size.height {
+ style.size.height = match style.size.width {
+ Length::Definite(DefiniteLength::Absolute(
+ AbsoluteLength::Pixels(width),
+ )) => Length::Definite(
+ px(image_size.height.0 as f32 * width.0
+ / image_size.width.0 as f32)
+ .into(),
+ ),
+ _ => Length::Definite(px(image_size.height.0 as f32).into()),
+ };
+ }
- if global_id.is_some() && data.frame_count() > 1 {
- cx.request_animation_frame();
+ if global_id.is_some() && data.frame_count() > 1 {
+ cx.request_animation_frame();
+ }
+ }
+ Some(_err) => {
+ if let Some(fallback) = self.style.fallback.as_ref() {
+ let mut element = fallback();
+ replacement_id = Some(element.request_layout(cx));
+ layout_state.replacement = Some(element);
+ }
+ if let Some(state) = &mut state {
+ state.started_loading = None;
+ }
+ }
+ None => {
+ if let Some(state) = &mut state {
+ if let Some((started_loading, _)) = state.started_loading {
+ if started_loading.elapsed() > LOADING_DELAY {
+ if let Some(loading) = self.style.loading.as_ref() {
+ let mut element = loading();
+ replacement_id = Some(element.request_layout(cx));
+ layout_state.replacement = Some(element);
+ }
+ }
+ } else {
+ let parent_view_id = cx.parent_view_id();
+ let task = cx.spawn(|mut cx| async move {
+ cx.background_executor().timer(LOADING_DELAY).await;
+ cx.update(|cx| {
+ cx.notify(parent_view_id);
+ })
+ .ok();
+ });
+ state.started_loading = Some((Instant::now(), task));
+ }
+ }
}
}
- cx.request_layout(style, [])
+ cx.request_layout(style, replacement_id)
});
- ((layout_id, frame_index), state)
+ layout_state.frame_index = frame_index;
+
+ ((layout_id, layout_state), state)
})
}
@@ -235,18 +374,24 @@ impl Element for Img {
&mut self,
global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
- _request_layout: &mut Self::RequestLayoutState,
+ request_layout: &mut Self::RequestLayoutState,
cx: &mut WindowContext,
- ) -> Option<Hitbox> {
+ ) -> Self::PrepaintState {
self.interactivity
- .prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
+ .prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, cx| {
+ if let Some(replacement) = &mut request_layout.replacement {
+ replacement.prepaint(cx);
+ }
+
+ hitbox
+ })
}
fn paint(
&mut self,
global_id: Option<&GlobalElementId>,
bounds: Bounds<Pixels>,
- frame_index: &mut Self::RequestLayoutState,
+ layout_state: &mut Self::RequestLayoutState,
hitbox: &mut Self::PrepaintState,
cx: &mut WindowContext,
) {
@@ -255,29 +400,26 @@ impl Element for Img {
.paint(global_id, bounds, hitbox.as_ref(), cx, |style, cx| {
let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
- if let Some(data) = source.use_data(cx) {
- let new_bounds = self.object_fit.get_bounds(bounds, data.size(*frame_index));
+ if let Some(Ok(data)) = source.use_data(cx) {
+ let new_bounds = self
+ .style
+ .object_fit
+ .get_bounds(bounds, data.size(layout_state.frame_index));
cx.paint_image(
new_bounds,
corner_radii,
data.clone(),
- *frame_index,
- self.grayscale,
+ layout_state.frame_index,
+ self.style.grayscale,
)
.log_err();
+ } else if let Some(replacement) = &mut layout_state.replacement {
+ replacement.paint(cx);
}
})
}
}
-impl IntoElement for Img {
- type Element = Self;
-
- fn into_element(self) -> Self::Element {
- self
- }
-}
-
impl Styled for Img {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.interactivity.base_style
@@ -290,41 +432,28 @@ impl InteractiveElement for Img {
}
}
-impl ImageSource {
- pub(crate) fn use_data(&self, cx: &mut WindowContext) -> Option<Arc<RenderImage>> {
- match self {
- ImageSource::Uri(_) | ImageSource::Embedded(_) | ImageSource::File(_) => {
- let uri_or_path: UriOrPath = match self {
- ImageSource::Uri(uri) => uri.clone().into(),
- ImageSource::File(path) => path.clone().into(),
- ImageSource::Embedded(path) => UriOrPath::Embedded(path.clone()),
- _ => unreachable!(),
- };
-
- cx.use_asset::<ImageAsset>(&uri_or_path)?.log_err()
- }
+impl IntoElement for Img {
+ type Element = Self;
- ImageSource::Render(data) => Some(data.to_owned()),
- ImageSource::Image(data) => cx.use_asset::<ImageDecoder>(data)?.log_err(),
- }
+ fn into_element(self) -> Self::Element {
+ self
}
+}
- /// Fetch the data associated with this source, using GPUI's asset caching
- pub async fn data(&self, cx: &mut AppContext) -> Option<Arc<RenderImage>> {
- match self {
- ImageSource::Uri(_) | ImageSource::Embedded(_) | ImageSource::File(_) => {
- let uri_or_path: UriOrPath = match self {
- ImageSource::Uri(uri) => uri.clone().into(),
- ImageSource::File(path) => path.clone().into(),
- ImageSource::Embedded(path) => UriOrPath::Embedded(path.clone()),
- _ => unreachable!(),
- };
+impl FocusableElement for Img {}
- cx.fetch_asset::<ImageAsset>(&uri_or_path).0.await.log_err()
- }
+impl StatefulInteractiveElement for Img {}
- ImageSource::Render(data) => Some(data.to_owned()),
- ImageSource::Image(data) => cx.fetch_asset::<ImageDecoder>(data).0.await.log_err(),
+impl ImageSource {
+ pub(crate) fn use_data(
+ &self,
+ cx: &mut WindowContext,
+ ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
+ match self {
+ ImageSource::Resource(resource) => cx.use_asset::<ImgResourceLoader>(&resource),
+ ImageSource::Custom(loading_fn) => loading_fn(cx),
+ ImageSource::Render(data) => Some(Ok(data.to_owned())),
+ ImageSource::Image(data) => cx.use_asset::<AssetLogger<ImageDecoder>>(data),
}
}
}
@@ -334,22 +463,23 @@ enum ImageDecoder {}
impl Asset for ImageDecoder {
type Source = Arc<Image>;
- type Output = Result<Arc<RenderImage>, Arc<anyhow::Error>>;
+ type Output = Result<Arc<RenderImage>, ImageCacheError>;
fn load(
source: Self::Source,
cx: &mut AppContext,
) -> impl Future<Output = Self::Output> + Send + 'static {
- let result = source.to_image_data(cx).map_err(Arc::new);
- async { result }
+ let renderer = cx.svg_renderer();
+ async move { source.to_image_data(renderer).map_err(Into::into) }
}
}
+/// An image loader for the GPUI asset system
#[derive(Clone)]
-enum ImageAsset {}
+pub enum ImageAssetLoader {}
-impl Asset for ImageAsset {
- type Source = UriOrPath;
+impl Asset for ImageAssetLoader {
+ type Source = Resource;
type Output = Result<Arc<RenderImage>, ImageCacheError>;
fn load(
@@ -363,12 +493,12 @@ impl Asset for ImageAsset {
let asset_source = cx.asset_source().clone();
async move {
let bytes = match source.clone() {
- UriOrPath::Path(uri) => fs::read(uri.as_ref())?,
- UriOrPath::Uri(uri) => {
+ Resource::Path(uri) => fs::read(uri.as_ref())?,
+ Resource::Uri(uri) => {
let mut response = client
.get(uri.as_ref(), ().into(), true)
.await
- .map_err(|e| ImageCacheError::Client(Arc::new(e)))?;
+ .map_err(|e| anyhow!(e))?;
let mut body = Vec::new();
response.body_mut().read_to_end(&mut body).await?;
if !response.status().is_success() {
@@ -383,13 +513,13 @@ impl Asset for ImageAsset {
}
body
}
- UriOrPath::Embedded(path) => {
+ Resource::Embedded(path) => {
let data = asset_source.load(&path).ok().flatten();
if let Some(data) = data {
data.to_vec()
} else {
return Err(ImageCacheError::Asset(
- format!("not found: {}", path).into(),
+ format!("Embedded resource not found: {}", path).into(),
));
}
}
@@ -450,9 +580,9 @@ impl Asset for ImageAsset {
/// An error that can occur when interacting with the image cache.
#[derive(Debug, Error, Clone)]
pub enum ImageCacheError {
- /// An error that occurred while fetching an image from a remote source.
- #[error("http error: {0}")]
- Client(#[from] Arc<anyhow::Error>),
+ /// Some other kind of error occurred
+ #[error("error: {0}")]
+ Other(#[from] Arc<anyhow::Error>),
/// An error that occurred while reading the image from disk.
#[error("IO error: {0}")]
Io(Arc<std::io::Error>),
@@ -477,20 +607,26 @@ pub enum ImageCacheError {
Usvg(Arc<usvg::Error>),
}
-impl From<std::io::Error> for ImageCacheError {
- fn from(error: std::io::Error) -> Self {
- Self::Io(Arc::new(error))
+impl From<anyhow::Error> for ImageCacheError {
+ fn from(value: anyhow::Error) -> Self {
+ Self::Other(Arc::new(value))
}
}
-impl From<ImageError> for ImageCacheError {
- fn from(error: ImageError) -> Self {
- Self::Image(Arc::new(error))
+impl From<io::Error> for ImageCacheError {
+ fn from(value: io::Error) -> Self {
+ Self::Io(Arc::new(value))
}
}
impl From<usvg::Error> for ImageCacheError {
- fn from(error: usvg::Error) -> Self {
- Self::Usvg(Arc::new(error))
+ fn from(value: usvg::Error) -> Self {
+ Self::Usvg(Arc::new(value))
+ }
+}
+
+impl From<image::ImageError> for ImageCacheError {
+ fn from(value: image::ImageError) -> Self {
+ Self::Image(Arc::new(value))
}
}
@@ -27,11 +27,11 @@ mod test;
mod windows;
use crate::{
- point, Action, AnyWindowHandle, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds,
- DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor,
- GPUSpecs, GlyphId, ImageSource, Keymap, LineLayout, Pixels, PlatformInput, Point,
- RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
- SharedString, Size, SvgSize, Task, TaskLabel, WindowContext, DEFAULT_WINDOW_SIZE,
+ point, Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels,
+ DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GPUSpecs, GlyphId,
+ ImageSource, Keymap, LineLayout, Pixels, PlatformInput, Point, RenderGlyphParams, RenderImage,
+ RenderImageParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, SvgRenderer,
+ SvgSize, Task, TaskLabel, WindowContext, DEFAULT_WINDOW_SIZE,
};
use anyhow::{anyhow, Result};
use async_task::Runnable;
@@ -1290,11 +1290,13 @@ impl Image {
/// Use the GPUI `use_asset` API to make this image renderable
pub fn use_render_image(self: Arc<Self>, cx: &mut WindowContext) -> Option<Arc<RenderImage>> {
- ImageSource::Image(self).use_data(cx)
+ ImageSource::Image(self)
+ .use_data(cx)
+ .and_then(|result| result.ok())
}
/// Convert the clipboard image to an `ImageData` object.
- pub fn to_image_data(&self, cx: &AppContext) -> Result<Arc<RenderImage>> {
+ pub fn to_image_data(&self, svg_renderer: SvgRenderer) -> Result<Arc<RenderImage>> {
fn frames_for_image(
bytes: &[u8],
format: image::ImageFormat,
@@ -1331,10 +1333,7 @@ impl Image {
ImageFormat::Bmp => frames_for_image(&self.bytes, image::ImageFormat::Bmp)?,
ImageFormat::Tiff => frames_for_image(&self.bytes, image::ImageFormat::Tiff)?,
ImageFormat::Svg => {
- // TODO: Fix this
- let pixmap = cx
- .svg_renderer()
- .render_pixmap(&self.bytes, SvgSize::ScaleFactor(1.0))?;
+ let pixmap = svg_renderer.render_pixmap(&self.bytes, SvgSize::ScaleFactor(1.0))?;
let buffer =
image::ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take())
@@ -5,5 +5,5 @@
pub use crate::{
util::FluentBuilder, BorrowAppContext, BorrowWindow, Context, Element, FocusableElement,
InteractiveElement, IntoElement, ParentElement, Refineable, Render, RenderOnce,
- StatefulInteractiveElement, Styled, VisualContext,
+ StatefulInteractiveElement, Styled, StyledImage, VisualContext,
};
@@ -10,7 +10,7 @@ pub(crate) struct RenderSvgParams {
}
#[derive(Clone)]
-pub(crate) struct SvgRenderer {
+pub struct SvgRenderer {
asset_source: Arc<dyn AssetSource>,
}
@@ -24,7 +24,7 @@ impl SvgRenderer {
Self { asset_source }
}
- pub fn render(&self, params: &RenderSvgParams) -> Result<Option<Vec<u8>>> {
+ pub(crate) fn render(&self, params: &RenderSvgParams) -> Result<Option<Vec<u8>>> {
if params.size.is_zero() {
return Err(anyhow!("can't render at a zero size"));
}
@@ -900,7 +900,13 @@ impl<'a> WindowContext<'a> {
/// Indicate that this view has changed, which will invoke any observers and also mark the window as dirty.
/// If this view or any of its ancestors are *cached*, notifying it will cause it or its ancestors to be redrawn.
- pub fn notify(&mut self, view_id: EntityId) {
+ /// Note that this method will always cause a redraw, the entire window is refreshed if view_id is None.
+ pub fn notify(&mut self, view_id: Option<EntityId>) {
+ let Some(view_id) = view_id else {
+ self.refresh();
+ return;
+ };
+
for view_id in self
.window
.rendered_frame
@@ -1165,13 +1171,7 @@ impl<'a> WindowContext<'a> {
/// If called from within a view, it will notify that view on the next frame. Otherwise, it will refresh the entire window.
pub fn request_animation_frame(&self) {
let parent_id = self.parent_view_id();
- self.on_next_frame(move |cx| {
- if let Some(parent_id) = parent_id {
- cx.notify(parent_id)
- } else {
- cx.refresh()
- }
- });
+ self.on_next_frame(move |cx| cx.notify(parent_id));
}
/// Spawn the future returned by the given closure on the application thread pool.
@@ -1982,9 +1982,7 @@ impl<'a> WindowContext<'a> {
///
/// Note that the multiple calls to this method will only result in one `Asset::load` call at a
/// time.
- ///
- /// This asset will not be cached by default, see [Self::use_cached_asset]
- pub fn use_asset<A: Asset + 'static>(&mut self, source: &A::Source) -> Option<A::Output> {
+ pub fn use_asset<A: Asset>(&mut self, source: &A::Source) -> Option<A::Output> {
let (task, is_first) = self.fetch_asset::<A>(source);
task.clone().now_or_never().or_else(|| {
if is_first {
@@ -1994,13 +1992,7 @@ impl<'a> WindowContext<'a> {
|mut cx| async move {
task.await;
- cx.on_next_frame(move |cx| {
- if let Some(parent_id) = parent_id {
- cx.notify(parent_id)
- } else {
- cx.refresh()
- }
- });
+ cx.on_next_frame(move |cx| cx.notify(parent_id));
}
})
.detach();
@@ -2163,6 +2155,9 @@ impl<'a> WindowContext<'a> {
/// A variant of `with_element_state` that allows the element's id to be optional. This is a convenience
/// method for elements where the element id may or may not be assigned. Prefer using `with_element_state`
/// when the element is guaranteed to have an id.
+ ///
+ /// The first option means 'no ID provided'
+ /// The second option means 'not yet initialized'
pub fn with_optional_element_state<S, R>(
&mut self,
global_id: Option<&GlobalElementId>,
@@ -4227,7 +4222,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
/// Indicate that this view has changed, which will invoke any observers and also mark the window as dirty.
/// If this view or any of its ancestors are *cached*, notifying it will cause it or its ancestors to be redrawn.
pub fn notify(&mut self) {
- self.window_cx.notify(self.view.entity_id());
+ self.window_cx.notify(Some(self.view.entity_id()));
}
/// Register a callback to be invoked when the window is resized.
@@ -370,7 +370,7 @@ impl Element for Scrollbar {
};
if let Some(id) = state.parent_id {
- cx.notify(id);
+ cx.notify(Some(id));
}
}
} else {
@@ -382,7 +382,7 @@ impl Element for Scrollbar {
if phase.bubble() {
state.drag.take();
if let Some(id) = state.parent_id {
- cx.notify(id);
+ cx.notify(Some(id));
}
}
});
@@ -5896,7 +5896,7 @@ pub fn client_side_decorations(element: impl IntoElement, cx: &mut WindowContext
let edge = cx.try_global::<GlobalResizeEdge>();
if new_edge != edge.map(|edge| edge.0) {
cx.window_handle()
- .update(cx, |workspace, cx| cx.notify(workspace.entity_id()))
+ .update(cx, |workspace, cx| cx.notify(Some(workspace.entity_id())))
.ok();
}
})