mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-28 03:37:00 -06:00
Merge main (e6d84c9d) into fix/explicit-join-ends-3283
This commit is contained in:
+24
-4
@@ -10,11 +10,31 @@ updates:
|
||||
interval: "daily"
|
||||
time: "06:00"
|
||||
timezone: "America/New_York"
|
||||
# Consolidate updates into a single PR rather than one PR per crate
|
||||
groups:
|
||||
cargo-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
# A dependency that matches several groups goes to the first one it matches, so the
|
||||
# lockstep families are listed before the catch-all cargo-patch group.
|
||||
# Families that must move together; all update types of a family land in one PR.
|
||||
egui-stack: # egui/eframe/winit are [patch.crates-io]'d to storytold forks: expect this PR red on upstream releases until the forks move
|
||||
patterns: ["egui*", "eframe", "epaint*", "ecolor", "emath", "winit", "wgpu*", "naga", "accesskit*"]
|
||||
font-stack:
|
||||
patterns: ["harfrust", "skrifa", "read-fonts", "font-types", "fontique", "parley", "write-fonts", "skera", "ttf-parser"]
|
||||
pdf-render-stack:
|
||||
patterns: ["hayro*", "krilla*", "vello*", "peniko", "kurbo", "resvg", "usvg", "tiny-skia*", "svgtypes"]
|
||||
icu-stack:
|
||||
patterns: ["icu_*", "fixed_decimal"]
|
||||
ml-stack:
|
||||
patterns: ["candle-*", "rten*", "ocrs"]
|
||||
platform-stack:
|
||||
patterns: ["zbus*", "zvariant*", "x11rb*"]
|
||||
apple-stack:
|
||||
patterns: ["objc2*", "block2", "dispatch2"]
|
||||
rustcrypto:
|
||||
patterns: ["aes", "sha1", "sha2", "sha3", "md-5", "hmac", "pbkdf2", "digest", "cipher"]
|
||||
# Everything else: patch bumps only (minor/major of other crates get one PR each).
|
||||
cargo-patch:
|
||||
update-types: ["patch"]
|
||||
patterns: ["*"]
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
|
||||
|
||||
@@ -352,20 +352,20 @@ The shared PDF backend keeps the existing standard-font and CMap features. These
|
||||
|
||||
| Crate / file | Author | Source | Licence | SHA-256 | Use |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitDingbats.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitDingbats.pfb | BSD-3-Clause | `845c752392b6c914fb989c75a08b7792b88f542d2499042ef2889f8c814a16ed` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitFixed.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitFixed.pfb | BSD-3-Clause | `b6c8fe53f134b8b6d4578cd2d544df4cee9624c4efa8d51a560fb40ea296101b` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitFixedBold.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitFixedBold.pfb | BSD-3-Clause | `f1b7159702973f54fc86254ea38bcf3712b2a736eb3a0995751e9f4bb45ad603` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitFixedBoldItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitFixedBoldItalic.pfb | BSD-3-Clause | `8a000945843bd31add06aee63bf9fd41b7578b4f3242a4b7cd46349ad9d24d4d` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitFixedItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitFixedItalic.pfb | BSD-3-Clause | `5007faf8320fa1fcc08b8894b356ad976f60b992ba29ee5272578bbdebaf3876` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSans.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitSans.pfb | BSD-3-Clause | `a02339e95d20db14cbdfc1c6baf359cd36a091d5434925c3a4040f60e1a1af9e` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSansBold.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitSansBold.pfb | BSD-3-Clause | `7bdfa36de1d5757dcfaf8b8a4be95c6e41c40fa70dbed9d78bf979a7b9959054` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSansBoldItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitSansBoldItalic.pfb | BSD-3-Clause | `555ce45924338e2e4b32821910221ce85da691fd59a9723c4c971a7c5627fc8b` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSansItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitSansItalic.pfb | BSD-3-Clause | `7751cbefeda351708dae8c4a88db043411bc39ab2ae2097685da845d4d16f307` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSerif.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitSerif.pfb | BSD-3-Clause | `4f57d2b9d884af8f907bf22df6019b52d86cbf6214fdbd02b1ae05472a543f35` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSerifBold.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitSerifBold.pfb | BSD-3-Clause | `0bdf4b04e964139818d51eda03d566ba999fc3ba2421b1c6c51f9dc969022e80` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSerifBoldItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitSerifBoldItalic.pfb | BSD-3-Clause | `a406cac82583bf98175cb62c87ed5e95c45fbb34eece63a10b0a13e793cb2e10` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSerifItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitSerifItalic.pfb | BSD-3-Clause | `610ae0687198045c4db5d1a6650fd5e92536631706382eb8b72e38df578d0ae9` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSymbol.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/FoxitSymbol.pfb | BSD-3-Clause | `47967d055530e7357088a08403115425643ec2cdfd6201ba8af0fbd7116c1539` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/CGATS001Compat-v2-micro.icc` | Clinton Ingram (saucecontrol/Compact-ICC-Profiles) | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/assets/CGATS001Compat-v2-micro.icc | CC0-1.0 | `73e1ba37d2bad5bab2a964f40a9eed96209666efc067c3322626214bbef234a0` | Default CMYK colour conversion (pinned hayro-interpret) |
|
||||
| `hayro-cmap 0.1.0 / assets/cmaps.brotli` | Adobe | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-cmap/assets/cmaps.brotli | BSD-3-Clause | `ddf37af521d904d5646c2fc37b2b7e912dc1b56f0d210190b61f598154758e46` | Character-code mapping tables required to read CJK PDFs. Non-visual dependency data; existing embedding unchanged |
|
||||
| `hayro-interpret 0.7.0 / src/font/generated/metrics.rs` | Adobe (Core 14 AFM metrics, PDF specification encodings); tables by The Hayro Authors | https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/hayro-interpret/src/font/generated/metrics.rs | MIT OR Apache-2.0 | `2ec9e608573eda459de1e1d9bda3bb494315e36bee885acc88912a11fe0caa41` | Glyph widths and encodings for non-embedded standard fonts. Non-visual dependency data; existing embedding unchanged |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitDingbats.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitDingbats.pfb | BSD-3-Clause | `845c752392b6c914fb989c75a08b7792b88f542d2499042ef2889f8c814a16ed` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitFixed.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitFixed.pfb | BSD-3-Clause | `b6c8fe53f134b8b6d4578cd2d544df4cee9624c4efa8d51a560fb40ea296101b` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitFixedBold.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitFixedBold.pfb | BSD-3-Clause | `f1b7159702973f54fc86254ea38bcf3712b2a736eb3a0995751e9f4bb45ad603` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitFixedBoldItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitFixedBoldItalic.pfb | BSD-3-Clause | `8a000945843bd31add06aee63bf9fd41b7578b4f3242a4b7cd46349ad9d24d4d` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitFixedItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitFixedItalic.pfb | BSD-3-Clause | `5007faf8320fa1fcc08b8894b356ad976f60b992ba29ee5272578bbdebaf3876` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSans.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitSans.pfb | BSD-3-Clause | `a02339e95d20db14cbdfc1c6baf359cd36a091d5434925c3a4040f60e1a1af9e` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSansBold.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitSansBold.pfb | BSD-3-Clause | `7bdfa36de1d5757dcfaf8b8a4be95c6e41c40fa70dbed9d78bf979a7b9959054` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSansBoldItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitSansBoldItalic.pfb | BSD-3-Clause | `555ce45924338e2e4b32821910221ce85da691fd59a9723c4c971a7c5627fc8b` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSansItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitSansItalic.pfb | BSD-3-Clause | `7751cbefeda351708dae8c4a88db043411bc39ab2ae2097685da845d4d16f307` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSerif.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitSerif.pfb | BSD-3-Clause | `4f57d2b9d884af8f907bf22df6019b52d86cbf6214fdbd02b1ae05472a543f35` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSerifBold.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitSerifBold.pfb | BSD-3-Clause | `0bdf4b04e964139818d51eda03d566ba999fc3ba2421b1c6c51f9dc969022e80` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSerifBoldItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitSerifBoldItalic.pfb | BSD-3-Clause | `a406cac82583bf98175cb62c87ed5e95c45fbb34eece63a10b0a13e793cb2e10` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSerifItalic.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitSerifItalic.pfb | BSD-3-Clause | `610ae0687198045c4db5d1a6650fd5e92536631706382eb8b72e38df578d0ae9` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/FoxitSymbol.pfb` | Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/FoxitSymbol.pfb | BSD-3-Clause | `47967d055530e7357088a08403115425643ec2cdfd6201ba8af0fbd7116c1539` | Renders non-embedded standard-14 fonts (pinned hayro-interpret) |
|
||||
| `hayro-interpret 0.7.0 / assets/CGATS001Compat-v2-micro.icc` | Clinton Ingram (saucecontrol/Compact-ICC-Profiles) | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/assets/CGATS001Compat-v2-micro.icc | CC0-1.0 | `73e1ba37d2bad5bab2a964f40a9eed96209666efc067c3322626214bbef234a0` | Default CMYK colour conversion (pinned hayro-interpret) |
|
||||
| `hayro-cmap 0.1.0 / assets/cmaps.brotli` | Adobe | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-cmap/assets/cmaps.brotli | BSD-3-Clause | `ddf37af521d904d5646c2fc37b2b7e912dc1b56f0d210190b61f598154758e46` | Character-code mapping tables required to read CJK PDFs. Non-visual dependency data; existing embedding unchanged |
|
||||
| `hayro-interpret 0.7.0 / src/font/generated/metrics.rs` | Adobe (Core 14 AFM metrics, PDF specification encodings); tables by The Hayro Authors | https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/hayro-interpret/src/font/generated/metrics.rs | MIT OR Apache-2.0 | `2ec9e608573eda459de1e1d9bda3bb494315e36bee885acc88912a11fe0caa41` | Glyph widths and encodings for non-embedded standard fonts. Non-visual dependency data; existing embedding unchanged |
|
||||
|
||||
Generated
+184
-15
@@ -803,6 +803,15 @@ dependencies = [
|
||||
"x509-cert",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.13.1"
|
||||
@@ -950,12 +959,40 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-docpath"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
|
||||
[[package]]
|
||||
name = "craft-fonts-build"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
|
||||
[[package]]
|
||||
name = "craft-i18n"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"fixed_decimal",
|
||||
"icu_decimal",
|
||||
"icu_locale_core",
|
||||
"icu_locale_fallback",
|
||||
"icu_normalizer",
|
||||
"icu_plurals",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"icu_provider_adapters",
|
||||
"icu_provider_blob",
|
||||
"log",
|
||||
"objc2-foundation 0.3.2",
|
||||
"sys-locale",
|
||||
"unicode-bidi",
|
||||
"windows-registry",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-keymap"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"egui",
|
||||
"serde",
|
||||
@@ -965,7 +1002,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-pdf"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"hayro-interpret",
|
||||
"hayro-syntax",
|
||||
@@ -976,11 +1013,11 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-print"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"craft-winprint-gdi",
|
||||
"miniz_oxide 0.8.9",
|
||||
"roxmltree 0.20.0",
|
||||
"miniz_oxide 0.9.1",
|
||||
"roxmltree 0.21.1",
|
||||
"tempfile",
|
||||
"winprint",
|
||||
"winsafe",
|
||||
@@ -989,20 +1026,28 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-ui"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"craft-fonts-build",
|
||||
"craft-i18n",
|
||||
"egui",
|
||||
"epaint",
|
||||
"js-sys",
|
||||
"log",
|
||||
"objc2 0.6.5",
|
||||
"objc2-app-kit 0.3.2",
|
||||
"objc2-foundation 0.3.2",
|
||||
"serde",
|
||||
"skrifa 0.44.0",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "craft-update"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=495e5971bc024aa96e6ed318cb6e7cd3a0103184#495e5971bc024aa96e6ed318cb6e7cd3a0103184"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dunce",
|
||||
@@ -1027,7 +1072,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-window"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"egui",
|
||||
"objc2 0.6.5",
|
||||
@@ -1599,6 +1644,17 @@ version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48"
|
||||
|
||||
[[package]]
|
||||
name = "fixed_decimal"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2214fb00f669ee96bee5bb29dfb1ddabe058b2bc426817efc79afa03c9bc55ae"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"smallvec",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
@@ -2117,7 +2173,7 @@ checksum = "9f4d0e94ddd48749f06bbe4e5389fb9799a0c45bcaf00495042076ef05e3241a"
|
||||
[[package]]
|
||||
name = "hayro-cmap"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"hayro-postscript",
|
||||
@@ -2126,7 +2182,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "hayro-interpret"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"hayro-cmap",
|
||||
@@ -2144,7 +2200,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "hayro-jbig2"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"fearless_simd",
|
||||
"hayro-ccitt",
|
||||
@@ -2162,12 +2218,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "hayro-postscript"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
|
||||
[[package]]
|
||||
name = "hayro-syntax"
|
||||
version = "0.7.2"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"hayro-ccitt",
|
||||
@@ -2337,6 +2393,23 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9eb8f655bba2d0c0459e43a5b0e6cd3cd388109898fb3d85d048165e8b0abd08"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_locale_core",
|
||||
"icu_pattern",
|
||||
"icu_plurals",
|
||||
"icu_provider",
|
||||
"serde",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.3.0"
|
||||
@@ -2382,6 +2455,9 @@ dependencies = [
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
@@ -2391,6 +2467,32 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_pattern"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b3db611446bd92c246b03e4aa0bafa71ddb194af431ddbb42b9d70e4bfce1a0"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"serde",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_plurals"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e475e6766ef87b1d3c1f97be6363995b0bfaeddbc789671615df598a97ff0593"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_provider",
|
||||
"serde",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.3.0"
|
||||
@@ -2420,6 +2522,7 @@ checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"postcard",
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"writeable",
|
||||
@@ -2429,6 +2532,30 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_adapters"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df887bc4f55dbc773c7c45a0c7dfa75be886f7b7812830a71ab7271cc1801820"
|
||||
dependencies = [
|
||||
"icu_locale_fallback",
|
||||
"icu_provider",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_blob"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5916b44fac319d3f58e4d8d805a627df19e27937b582cd99fcde3bb6b7ce1b0e"
|
||||
dependencies = [
|
||||
"icu_provider",
|
||||
"postcard",
|
||||
"serde",
|
||||
"writeable",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter"
|
||||
version = "2.3.0"
|
||||
@@ -2704,7 +2831,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "krilla"
|
||||
version = "0.8.2"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=a0916808f7d78bed8c7b90cdf937176015454a7f#a0916808f7d78bed8c7b90cdf937176015454a7f"
|
||||
source = "git+https://github.com/storytold/craft-libs?rev=92d392cff132eaf53e13afff77f38b5c11b73aa0#92d392cff132eaf53e13afff77f38b5c11b73aa0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bumpalo",
|
||||
@@ -2834,6 +2961,9 @@ name = "litemap"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
@@ -3809,6 +3939,16 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.6"
|
||||
@@ -5223,6 +5363,18 @@ dependencies = [
|
||||
"syn 3.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sys-locale"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.7.0"
|
||||
@@ -5853,6 +6005,12 @@ dependencies = [
|
||||
"xmlwriter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-zero"
|
||||
version = "0.8.1"
|
||||
@@ -7379,11 +7537,20 @@ dependencies = [
|
||||
"read-fonts 0.39.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
@@ -7705,6 +7872,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"serde_core",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
|
||||
+13
-13
@@ -11,15 +11,15 @@ rust-version = "1.95"
|
||||
repository = "https://github.com/storytold/vectorcraft"
|
||||
|
||||
[workspace.dependencies]
|
||||
craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f", default-features = false, features = ["interpret", "write"] }
|
||||
craft-print = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" }
|
||||
craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" }
|
||||
craft-ui = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f", features = ["serde"] }
|
||||
craft-keymap = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" }
|
||||
craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", default-features = false, features = ["interpret", "write"] }
|
||||
craft-print = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
craft-ui = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", features = ["serde"] }
|
||||
craft-keymap = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
# Decides who draws the window frame (desktop vs. app) so the suite shares one native titlebar per
|
||||
# desktop. The `egui` feature adds `craft_window::decorate`. (craft-libs#217.)
|
||||
craft-window = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f", features = ["egui"] }
|
||||
craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "495e5971bc024aa96e6ed318cb6e7cd3a0103184", features = ["egui"] }
|
||||
craft-window = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", features = ["egui"] }
|
||||
craft-update = { git = "https://github.com/storytold/craft-libs", version = "=0.1.0", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", features = ["egui"] }
|
||||
# internal
|
||||
vectorcraft-geom = { path = "crates/geom" }
|
||||
vectorcraft-color = { path = "crates/color" }
|
||||
@@ -141,12 +141,12 @@ lto = "thin"
|
||||
codegen-units = 1
|
||||
|
||||
[patch.crates-io]
|
||||
hayro-syntax = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" }
|
||||
hayro-interpret = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" }
|
||||
hayro-jbig2 = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" }
|
||||
hayro-cmap = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" }
|
||||
hayro-postscript = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" }
|
||||
krilla = { git = "https://github.com/storytold/craft-libs", rev = "a0916808f7d78bed8c7b90cdf937176015454a7f" }
|
||||
hayro-syntax = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
hayro-interpret = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
hayro-jbig2 = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
hayro-cmap = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
hayro-postscript = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
krilla = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0" }
|
||||
ecolor = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
||||
emath = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
||||
epaint = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }
|
||||
|
||||
@@ -77,10 +77,10 @@ Shared PDF backend
|
||||
|
||||
The patched hayro-interpret 0.7.0, hayro-syntax 0.7.2, hayro-jbig2 0.3.0, hayro-cmap 0.1.0
|
||||
and hayro-postscript 0.1.0 are fetched from https://github.com/storytold/craft-libs at revision
|
||||
a0916808f7d78bed8c7b90cdf937176015454a7f. Their upstream project is
|
||||
92d392cff132eaf53e13afff77f38b5c11b73aa0. Their upstream project is
|
||||
https://github.com/LaurenzV/hayro, licensed MIT OR Apache-2.0. The snapshot retains each
|
||||
crate's upstream licence files. The patch ledger is at
|
||||
https://github.com/storytold/craft-libs/blob/a0916808f7d78bed8c7b90cdf937176015454a7f/vendor/README.craft-reader.md.
|
||||
https://github.com/storytold/craft-libs/blob/92d392cff132eaf53e13afff77f38b5c11b73aa0/vendor/README.craft-reader.md.
|
||||
|
||||
Assets already bundled through Hayro's default features retain their licences:
|
||||
- Foxit standard-14 substitutes: Copyright 2014 PDFium Authors. All rights reserved.
|
||||
|
||||
@@ -3,3 +3,6 @@
|
||||
allow-unwrap-in-tests = true
|
||||
allow-expect-in-tests = true
|
||||
allow-panic-in-tests = true
|
||||
disallowed-types = [
|
||||
{ path = "egui::ComboBox", reason = "use craft_ui::ComboBox: egui's combo box menu can't grow back while open (egui#5225)" },
|
||||
]
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn status_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let print = app.session.prefs.display_print_size;
|
||||
let shown = crate::state::zoom_display_percent(zoom, print);
|
||||
ui.name_inputs(tl!("Zoom"), |ui| {
|
||||
egui::ComboBox::from_id_salt("zoom-combo")
|
||||
craft_ui::ComboBox::from_id_salt("zoom-combo")
|
||||
.selected_text(egui::RichText::new(zoom_label(zoom, print)).size(11.5))
|
||||
.width(76.0)
|
||||
.show_ui(ui, |ui| {
|
||||
@@ -36,7 +36,7 @@ pub fn status_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
});
|
||||
let rot = app.view().map(|v| v.rotation).unwrap_or(0.0);
|
||||
ui.name_inputs(tl!("Rotation"), |ui| {
|
||||
egui::ComboBox::from_id_salt("rot-combo")
|
||||
craft_ui::ComboBox::from_id_salt("rot-combo")
|
||||
.selected_text(egui::RichText::new(format!("{rot:.0}°")).size(11.5))
|
||||
.width(52.0)
|
||||
.show_ui(ui, |ui| {
|
||||
|
||||
@@ -207,7 +207,7 @@ pub(super) fn artboard_list(app: &VectorcraftApp, ui: &mut Ui, cur: usize) -> Op
|
||||
ui.add_enabled_ui(!boards.is_empty(), |ui| {
|
||||
ui.set_max_width(FIELD_W);
|
||||
ui.name_inputs(tl!("Artboard"), |ui| {
|
||||
egui::ComboBox::from_id_salt("artboard-list")
|
||||
craft_ui::ComboBox::from_id_salt("artboard-list")
|
||||
.selected_text(egui::RichText::new(shown).size(11.5))
|
||||
.width(FIELD_W)
|
||||
.wrap_mode(egui::TextWrapMode::Truncate)
|
||||
|
||||
@@ -238,7 +238,7 @@ pub fn contributors_ui(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
|
||||
ui.separator();
|
||||
ui.label(tl!("Sort"));
|
||||
ui.name_inputs(tl!("Sort"), |ui| {
|
||||
egui::ComboBox::from_id_salt("credits_sort").selected_text(v.key.label().0).show_ui(ui, |ui| {
|
||||
craft_ui::ComboBox::from_id_salt("credits_sort").selected_text(v.key.label().0).show_ui(ui, |ui| {
|
||||
for k in SortKey::ALL {
|
||||
if ui.selectable_label(v.key == k, k.label().0).clicked() {
|
||||
v.key = k;
|
||||
|
||||
@@ -101,7 +101,7 @@ fn precision(ui: &mut Ui, fields: &mut serde_json::Map<String, Value>, id: &str)
|
||||
let mut value = before;
|
||||
ui.label(tl!("Precision:"));
|
||||
ui.name_inputs(tl!("Precision"), |ui| {
|
||||
egui::ComboBox::from_id_salt(id).selected_text(value.to_string()).width(55.0).show_ui(ui, |ui| {
|
||||
craft_ui::ComboBox::from_id_salt(id).selected_text(value.to_string()).width(55.0).show_ui(ui, |ui| {
|
||||
for n in 0..=6 {
|
||||
ui.selectable_value(&mut value, n, n.to_string());
|
||||
}
|
||||
@@ -136,7 +136,7 @@ fn body(app: &mut VectorcraftApp, ui: &mut Ui, d: &mut Dialog) -> bool {
|
||||
f.insert("scale".into(), json!(scale));
|
||||
}
|
||||
ui.name_inputs(tl!("Scale Presets"), |ui| {
|
||||
egui::ComboBox::from_id_salt("dimension-scales").selected_text(tl!("Presets")).width(76.0).show_ui(ui, |ui| {
|
||||
craft_ui::ComboBox::from_id_salt("dimension-scales").selected_text(tl!("Presets")).width(76.0).show_ui(ui, |ui| {
|
||||
for scale in ["1:1", "1:2", "1:5", "1:10", "1:20", "1:50", "1:100", "1:200", "2:1", "5:1", "10:1"] {
|
||||
if ui.selectable_label(false, scale).clicked() {
|
||||
f.insert("scale".into(), json!(scale));
|
||||
|
||||
@@ -122,7 +122,7 @@ const CHIP: egui::Vec2 = egui::vec2(16.0, 10.0);
|
||||
fn color_dropdown(ui: &mut egui::Ui, shown: &str) -> Option<usize> {
|
||||
let border = crate::theme::Tokens::get(ui.ctx()).input_border;
|
||||
let rows = LAYER_COLORS.iter().map(|(n, c)| (*n, Some(*c))).chain([("Custom", None)]);
|
||||
widgets::combo(ui, "layer-color", tl!(shown), 140.0, false, |ui| {
|
||||
widgets::combo(ui, "layer-color", tl!(shown), 140.0, |ui| {
|
||||
let mut chosen = None;
|
||||
for (i, (name, rgb)) in rows.enumerate() {
|
||||
let chip = egui::IdSalt::new(("layer-color-chip", i));
|
||||
|
||||
@@ -923,40 +923,51 @@ fn rows_section(ui: &mut egui::Ui, d: &mut Dialog, review: &mut Review, statuses
|
||||
list.inner.inner_rect.height()
|
||||
}
|
||||
|
||||
/// Row `r`'s target picker: "No equivalent" and the row's choices, then Keyboard Shortcuts' entries
|
||||
/// that match the search. → the target picked ("" for no equivalent).
|
||||
fn target_picker(ui: &mut egui::Ui, i: usize, r: &Row) -> Option<String> {
|
||||
/// Row `r`'s target picker: the search field, "No equivalent" and the row's choices stay at the
|
||||
/// top, over Keyboard Shortcuts' entries that match the search, which scroll. → the target picked
|
||||
/// ("" for no equivalent).
|
||||
pub(super) fn target_picker(ui: &mut egui::Ui, i: usize, r: &Row) -> Option<String> {
|
||||
let current = target_label(r.target.as_deref());
|
||||
widgets::combo(ui, ("shortcut-import-target", i), ¤t, TARGET_W, true, |ui| {
|
||||
let field = ui.scope_id().with("query");
|
||||
if ui.memory(|m| m.focused().is_none()) {
|
||||
ui.memory_mut(|m| m.request_focus(field.with("edit")));
|
||||
}
|
||||
let q = widgets::search_field(ui, field, tl!("Search")).to_lowercase();
|
||||
let mut picked = None;
|
||||
let mut option = |ui: &mut egui::Ui, key: &str, label: &str| {
|
||||
if ui.add(egui::Button::selectable(r.target.as_deref() == Some(key), label)).clicked() {
|
||||
picked = Some(key.to_string());
|
||||
}
|
||||
};
|
||||
option(ui, "", tl!("No equivalent"));
|
||||
let list: Vec<&Entry> = if q.is_empty() {
|
||||
for e in r.choices.iter().filter_map(|c| entry(c)) {
|
||||
option(ui, &e.key, &shown(e));
|
||||
}
|
||||
ui.separator();
|
||||
shortcut_editor::entries().iter().collect()
|
||||
} else {
|
||||
shortcut_editor::entries().iter().filter(|e| shown(e).to_lowercase().contains(&q) || e.key.to_lowercase().contains(&q)).collect()
|
||||
};
|
||||
egui::ScrollArea::vertical().max_height(300.0).show_rows(ui, ROW_H, list.len(), |ui, range| {
|
||||
for e in range.filter_map(|k| list.get(k)) {
|
||||
option(ui, &e.key, &shown(e));
|
||||
}
|
||||
});
|
||||
if picked.is_some() {
|
||||
ui.close();
|
||||
}
|
||||
picked
|
||||
// The entries' list is up to 300 points tall under the header, which was this tall last frame.
|
||||
let header_id = egui::Id::unique(("shortcut-import-target-header", i));
|
||||
let height = ui.data(|d| d.get_temp::<f32>(header_id)).unwrap_or(0.0) + 300.0;
|
||||
let option = |ui: &mut egui::Ui, key: &str, label: &str| {
|
||||
ui.add(egui::Button::selectable(r.target.as_deref() == Some(key), label)).clicked().then(|| key.to_string())
|
||||
};
|
||||
widgets::combo_menu(ui, ("shortcut-import-target", i), ¤t, TARGET_W, |menu| {
|
||||
menu.height(height).show_rows_with_header(
|
||||
|ui| {
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
let field = ui.scope_id().with("query");
|
||||
if ui.memory(|m| m.focused().is_none()) {
|
||||
ui.memory_mut(|m| m.request_focus(field.with("edit")));
|
||||
}
|
||||
let q = widgets::search_field(ui, field, tl!("Search")).to_lowercase();
|
||||
let mut picked = option(ui, "", tl!("No equivalent"));
|
||||
let list: Vec<&Entry> = if q.is_empty() {
|
||||
for e in r.choices.iter().filter_map(|c| entry(c)) {
|
||||
picked = option(ui, &e.key, &shown(e)).or(picked);
|
||||
}
|
||||
ui.separator();
|
||||
shortcut_editor::entries().iter().collect()
|
||||
} else {
|
||||
shortcut_editor::entries().iter().filter(|e| shown(e).to_lowercase().contains(&q) || e.key.to_lowercase().contains(&q)).collect()
|
||||
};
|
||||
ui.data_mut(|d| d.insert_temp(header_id, ui.min_rect().height()));
|
||||
(list, picked)
|
||||
},
|
||||
ROW_H,
|
||||
|(list, _)| list.len(),
|
||||
|ui, range, (list, mut picked)| {
|
||||
for e in range.filter_map(|k| list.get(k)) {
|
||||
picked = option(ui, &e.key, &shown(e)).or(picked);
|
||||
}
|
||||
if picked.is_some() {
|
||||
ui.close();
|
||||
}
|
||||
picked
|
||||
},
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use craft_keymap::import::namematch::Candidate;
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_engine::Session;
|
||||
|
||||
use super::shortcut_import::{Code, KIND, Review, Row, Status, report};
|
||||
use super::shortcut_import::{Code, KIND, Review, Row, Status, report, target_picker};
|
||||
use super::*;
|
||||
use crate::{io, shortcut_editor, theme};
|
||||
|
||||
@@ -576,3 +576,58 @@ fn long_names_match_and_apply() {
|
||||
let (out, ov) = apply_rows(&mut app, "defaults", &[(&name, "Menus", "edit.copy", "Cmd+Alt+Shift+F11")]);
|
||||
assert_eq!((out["applied"].clone(), ov), (json!(1), json!({"edit.copy": "Cmd+Alt+Shift+F11"})));
|
||||
}
|
||||
|
||||
/// A search in a row's target picker that matches nothing shrinks its menu to the search field
|
||||
/// and "No equivalent"; clearing the search lists every entry again, and the menu grows back to
|
||||
/// its height before the search (egui#5225). The search field stays where it was throughout.
|
||||
#[test]
|
||||
fn the_target_picker_grows_back_when_a_search_that_matched_nothing_is_cleared() {
|
||||
let ctx = egui::Context::default();
|
||||
theme::install_fonts(&ctx);
|
||||
theme::apply(&ctx, Default::default());
|
||||
let r = Row { name: "Acme Undo".into(), ..Default::default() };
|
||||
let frame = |events: Vec<egui::Event>| {
|
||||
let input =
|
||||
egui::RawInput { events, screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(600.0, 800.0))), ..Default::default() };
|
||||
let mut at = egui::Pos2::ZERO;
|
||||
let mut out = ctx.run_ui(input, |ui| {
|
||||
at = ui.next_widget_position();
|
||||
target_picker(ui, 0, &r);
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
let text = |label: &str| {
|
||||
out.shapes.iter().find_map(|s| match &s.shape {
|
||||
egui::Shape::Text(t) if t.galley.text() == label => Some(t.pos),
|
||||
_ => None,
|
||||
})
|
||||
};
|
||||
// The row under the search field: it shows whatever the field holds.
|
||||
let none_row = text("No equivalent");
|
||||
let menu = none_row.and_then(|p| ctx.layer_id_at(p)).and_then(|l| ctx.memory(|m| m.area_rect(l.id)));
|
||||
(at, none_row, text("Search"), menu.map(|r| r.height()))
|
||||
};
|
||||
let settle = |events: Vec<egui::Event>| {
|
||||
let mut last = frame(events);
|
||||
for _ in 0..5 {
|
||||
last = frame(vec![]);
|
||||
}
|
||||
last
|
||||
};
|
||||
let (at, ..) = frame(vec![]);
|
||||
let click = at + egui::vec2(40.0, 10.0);
|
||||
let button = |pressed| egui::Event::PointerButton { pos: click, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
frame(vec![egui::Event::PointerMoved(click), button(true)]);
|
||||
let (_, none_row, search, full) = settle(vec![button(false)]);
|
||||
let (none_row, search, full) = (none_row.expect("the menu is open"), search.expect("the search field shows"), full.expect("the menu's area"));
|
||||
let nothing = "qqzzxxqqzz";
|
||||
let (_, n, _, small) = settle(vec![egui::Event::Text(nothing.into())]);
|
||||
assert_eq!(n, Some(none_row), "the search field stays put while nothing matches");
|
||||
let small = small.expect("the menu stays open");
|
||||
assert!(small + 40.0 < full, "the menu shrinks to its search field: {small} of {full}");
|
||||
for _ in nothing.chars() {
|
||||
frame(vec![press(egui::Key::Backspace)]);
|
||||
}
|
||||
let (_, n, s, grown) = settle(vec![]);
|
||||
assert_eq!((n, s), (Some(none_row), Some(search)), "the search field is where it was");
|
||||
assert_eq!(grown, Some(full), "the menu grows back to its height before the search");
|
||||
}
|
||||
|
||||
@@ -555,10 +555,10 @@ fn warp_options_style_keyboard_enter_does_not_accept_dialog() {
|
||||
assert_ne!(ctx.memory(|m| m.focused()), Some(style_id));
|
||||
warp_key(&mut app, &ctx, egui::Key::Tab, egui::Modifiers::SHIFT);
|
||||
assert_eq!(ctx.memory(|m| m.focused()), Some(style_id));
|
||||
assert!(!egui::ComboBox::is_open(&ctx, style_id));
|
||||
assert!(!craft_ui::ComboBox::is_open(&ctx, style_id));
|
||||
warp_key(&mut app, &ctx, egui::Key::Enter, egui::Modifiers::NONE);
|
||||
assert!(app.ui.dialog.is_some(), "WARP_STYLE_ENTER_OPEN: opening Style must not accept Warp Options");
|
||||
assert!(egui::ComboBox::is_open(&ctx, style_id));
|
||||
assert!(craft_ui::ComboBox::is_open(&ctx, style_id));
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().str("__effect"), "warp.arc");
|
||||
assert_eq!(entries(&app.session.doc().unwrap().history.undo), undo);
|
||||
assert_eq!(entries(&app.session.doc().unwrap().history.redo), redo);
|
||||
@@ -567,7 +567,7 @@ fn warp_options_style_keyboard_enter_does_not_accept_dialog() {
|
||||
tab_to_warp_control(&mut app, &ctx, "Arc Lower", egui::Modifiers::NONE);
|
||||
warp_key(&mut app, &ctx, egui::Key::Enter, egui::Modifiers::NONE);
|
||||
assert!(app.ui.dialog.is_some(), "WARP_STYLE_ENTER_CHOICE: choosing Style must not accept Warp Options");
|
||||
assert!(!egui::ComboBox::is_open(&ctx, style_id));
|
||||
assert!(!craft_ui::ComboBox::is_open(&ctx, style_id));
|
||||
assert_eq!(app.ui.dialog.as_ref().unwrap().str("__effect"), "warp.arcLower");
|
||||
assert!(app.ui.dialog.as_ref().unwrap().bool("__warp_style_changed"));
|
||||
let state = app.session.doc().unwrap();
|
||||
|
||||
@@ -71,7 +71,7 @@ pub(super) fn body(ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
let current = WARP_STYLES.iter().find(|(style, _)| *style == suffix).map_or_else(|| suffix.to_string(), |(_, label)| shown(label));
|
||||
widgets::label_row(ui, tl!("Style:"), LABEL_W, |ui| {
|
||||
style_preview(ui, suffix);
|
||||
if let Some((style, label)) = widgets::combo(ui, "warp-style", ¤t, 170.0, false, |ui| {
|
||||
if let Some((style, label)) = widgets::combo(ui, "warp-style", ¤t, 170.0, |ui| {
|
||||
let mut chosen = None;
|
||||
for (style, label) in WARP_STYLES {
|
||||
ui.horizontal(|ui| {
|
||||
|
||||
+148
-108
@@ -82,6 +82,8 @@ const STAR: f32 = 18.0;
|
||||
const NAME_SHARE: f32 = 0.5;
|
||||
/// Width of the disclosure triangle of a family with several styles.
|
||||
const DISCLOSURE: f32 = 14.0;
|
||||
/// The list's greatest height, under the search field and filters.
|
||||
const LIST_HEIGHT: f32 = 600.0;
|
||||
/// Samples rendered (or asked for) per frame, across every menu, so a long list doesn't stall the UI.
|
||||
const SAMPLES_PER_FRAME: usize = 4;
|
||||
/// Samples rendering at once (native).
|
||||
@@ -231,8 +233,8 @@ struct MenuState {
|
||||
/// The rows listed, and what for: built again only when that changes, not every frame.
|
||||
rows: Arc<[Row]>,
|
||||
rows_key: Option<RowsKey>,
|
||||
/// The list's scroll offset last frame.
|
||||
scrolled: f32,
|
||||
/// The height of the search field and filters last frame.
|
||||
header: f32,
|
||||
}
|
||||
|
||||
/// The font family menu showing `current`, `width` points wide, rows sampling `sample` (the
|
||||
@@ -247,12 +249,21 @@ pub fn font_menu(
|
||||
) -> Option<FontPick> {
|
||||
let state_id = ui.scope_id().with(&id).with("font-menu");
|
||||
let mut st: MenuState = ui.data(|d| d.get_temp(state_id)).unwrap_or_default();
|
||||
let mut drawn = false;
|
||||
let picked = widgets::combo(ui, &id, current, width, true, |ui| {
|
||||
drawn = true;
|
||||
list(ui, state_id, &mut st, current, sample, look)
|
||||
let height = st.header + LIST_HEIGHT;
|
||||
let state = &mut st;
|
||||
// The search field and filters stay in place over the rows, which scroll. The rows are all
|
||||
// one height (a family's styles too), so only those in view are laid out: there are thousands
|
||||
// of families.
|
||||
let shown = widgets::combo_menu(ui, &id, current, width, |menu| {
|
||||
menu.height(height).show_rows_with_header(
|
||||
|ui| header(ui, state_id, state, current, look),
|
||||
look.row,
|
||||
|h| h.rows.len(),
|
||||
|ui, range, h| rows(ui, range, h, sample, look),
|
||||
)
|
||||
});
|
||||
let mut out = picked;
|
||||
let drawn = shown.is_some();
|
||||
let mut out = shown.flatten();
|
||||
if !drawn && out.is_none() && st.previewed.take().is_some() {
|
||||
// Closed (Escape, a click outside) with a preview showing.
|
||||
out = Some(FontPick::EndPreview);
|
||||
@@ -264,10 +275,33 @@ pub fn font_menu(
|
||||
out
|
||||
}
|
||||
|
||||
/// The list inside the open menu.
|
||||
/// What the menu's header hands its rows.
|
||||
struct Listed<'s> {
|
||||
st: &'s mut MenuState,
|
||||
/// The rows listed.
|
||||
rows: Arc<[Row]>,
|
||||
/// The current family by its own name.
|
||||
current: String,
|
||||
/// Enter was pressed; ↑/↓/←/→ moved the highlight; the menu is opening.
|
||||
enter: bool,
|
||||
keyed: bool,
|
||||
opening: bool,
|
||||
}
|
||||
|
||||
/// The menu's contents outside a menu, for the unit tests: the header over its rows, as
|
||||
/// [`craft_ui::combo::Menu::show_rows_with_header`] lays them out.
|
||||
#[cfg(test)]
|
||||
fn list(ui: &mut Ui, state_id: egui::Id, st: &mut MenuState, current: &str, sample: Option<&str>, look: MenuLook) -> Option<FontPick> {
|
||||
let row_h = look.row;
|
||||
let h = header(ui, state_id, st, current, look);
|
||||
let total = h.rows.len();
|
||||
egui::ScrollArea::vertical().max_height(ui.available_height()).show_rows(ui, look.row, total, |ui, range| rows(ui, range, h, sample, look)).inner
|
||||
}
|
||||
|
||||
/// The top of the open menu, which stays in place: the search field, the filters, and the rows
|
||||
/// they list.
|
||||
fn header<'s>(ui: &mut Ui, state_id: egui::Id, st: &'s mut MenuState, current: &str, look: MenuLook) -> Listed<'s> {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
// Each time the menu opens: no query, the current family highlighted and in view (the
|
||||
// filters stay as they were).
|
||||
let pass = ui.ctx().cumulative_pass_nr();
|
||||
@@ -407,122 +441,128 @@ fn list(ui: &mut Ui, state_id: egui::Id, st: &mut MenuState, current: &str, samp
|
||||
}
|
||||
keyed = true;
|
||||
}
|
||||
st.header = ui.min_rect().height();
|
||||
if rows.is_empty() {
|
||||
widgets::dim_label(ui, tl!("No matching fonts"));
|
||||
}
|
||||
ui.spacing_mut().item_spacing.y = 0.0;
|
||||
// The scroll bar beside the rows, not over them: a floating one covered the stars at their
|
||||
// right ends, so a favourite was hard to click (#2277).
|
||||
ui.spacing_mut().scroll = widgets::list_scroll_style();
|
||||
Listed { st, rows, current: current.to_string(), enter, keyed, opening }
|
||||
}
|
||||
|
||||
/// The rows of the open menu in `range`, the ones in view: the menu's one scrolling part, under
|
||||
/// the search field and filters (#555).
|
||||
fn rows(ui: &mut Ui, range: std::ops::Range<usize>, h: Listed<'_>, sample: Option<&str>, look: MenuLook) -> Option<FontPick> {
|
||||
let Listed { st, rows, current, enter, keyed, opening } = h;
|
||||
let current = current.as_str();
|
||||
let row_h = look.row;
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let db = vectorcraft_text::FontDb::global();
|
||||
let favorite = |f: &str| look.favorites.iter().any(|x| x.eq_ignore_ascii_case(f));
|
||||
if rows.is_empty() {
|
||||
// A filter or a font rescan can remove the row whose font was previewed.
|
||||
// Do not leave that font applied while the list has no candidate to highlight.
|
||||
return st.previewed.take().map(|_| FontPick::EndPreview);
|
||||
}
|
||||
// The rows: only those in view are laid out (there are thousands of families). The list is
|
||||
// the menu's one scrolling part, under the search field and filters (#555): it fills the
|
||||
// menu's room (its max rect) below them.
|
||||
let room = (ui.max_rect().bottom() - ui.next_widget_position().y).clamp(120.0, 600.0);
|
||||
let mut area = egui::ScrollArea::vertical().max_height(room).min_scrolled_height(room.min(row_h * rows.len() as f32));
|
||||
// Scrolls the list to row `i`'s place, laid out or not: the rows in view start at
|
||||
// `range.start`.
|
||||
let (left, width) = (ui.max_rect().left(), ui.max_rect().width());
|
||||
let top = ui.max_rect().top() - range.start as f32 * row_h;
|
||||
let scroll_to = |ui: &Ui, i: usize, align| {
|
||||
let place = egui::Rect::from_min_size(egui::pos2(left, top + i as f32 * row_h), vec2(width, row_h));
|
||||
ui.scroll_to_rect_animation(place, align, egui::style::ScrollAnimation::none());
|
||||
};
|
||||
// Open at the recent section even when the current family is farther down the list.
|
||||
// Otherwise centre the current font; subsequent key presses keep the highlight in view.
|
||||
if opening && matches!(rows.first(), Some(Row::Heading("Recent Fonts"))) {
|
||||
area = area.vertical_scroll_offset(0.0);
|
||||
} else if let Some(top) = st.highlight.map(|i| i as f32 * row_h) {
|
||||
let target = if opening {
|
||||
Some(top - (room - row_h) * 0.5)
|
||||
} else if keyed && top < st.scrolled {
|
||||
Some(top)
|
||||
} else if keyed && top + row_h > st.scrolled + room {
|
||||
Some(top + row_h - room)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(y) = target {
|
||||
area = area.vertical_scroll_offset(y.max(0.0));
|
||||
}
|
||||
scroll_to(ui, 0, Some(egui::Align::TOP));
|
||||
} else if let Some(i) = st.highlight
|
||||
&& (opening || keyed)
|
||||
{
|
||||
scroll_to(ui, i, opening.then_some(egui::Align::Center));
|
||||
}
|
||||
let mut chosen: Option<(String, Option<String>)> = None;
|
||||
let mut hovered: Option<usize> = None;
|
||||
let mut toggled: Option<String> = None;
|
||||
let mut starred: Option<String> = None;
|
||||
ui.spacing_mut().item_spacing.y = 0.0;
|
||||
// The scroll bar beside the rows, not over them: a floating one covered the stars at their
|
||||
// right ends, so a favourite was hard to click (#2277).
|
||||
ui.spacing_mut().scroll = widgets::list_scroll_style();
|
||||
let shown = area.show_rows(ui, row_h, rows.len(), |ui, range| {
|
||||
for i in range {
|
||||
let Some(row) = rows.get(i) else { continue };
|
||||
if let Row::Heading(label) = row {
|
||||
let (rect, _) = ui.allocate_exact_size(vec2(ui.available_width(), row_h), Sense::hover());
|
||||
// Existing translated menu labels; the heading cannot preview or apply a font.
|
||||
let label = match *label {
|
||||
"Recent Fonts" => tl!("Recent Fonts"),
|
||||
_ => tl!("Fonts"),
|
||||
};
|
||||
ui.painter_at(rect).text(
|
||||
rect.left_center() + vec2(4.0, 0.0),
|
||||
egui::Align2::LEFT_CENTER,
|
||||
label,
|
||||
egui::FontId::proportional(11.0),
|
||||
t.text_dim,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let Some(family) = row.family() else { continue };
|
||||
let (rect, resp) = ui.allocate_exact_size(vec2(ui.available_width(), row_h), Sense::click());
|
||||
if resp.hovered() && ui.input(|inp| inp.pointer.delta() != egui::Vec2::ZERO || inp.smooth_scroll_delta != egui::Vec2::ZERO) {
|
||||
hovered = Some(i);
|
||||
}
|
||||
let p = ui.painter_at(rect);
|
||||
if st.highlight == Some(i) {
|
||||
p.rect_filled(rect, 2.0, t.selection);
|
||||
} else if matches!(row, Row::Family(f) if f.eq_ignore_ascii_case(current)) {
|
||||
p.rect_filled(rect, 2.0, t.hover);
|
||||
}
|
||||
// The disclosure triangle of a family with several styles.
|
||||
let tri = egui::Rect::from_min_size(rect.min, vec2(DISCLOSURE, row_h));
|
||||
let mut on_triangle = false;
|
||||
if let Row::Family(f) = row
|
||||
&& db.styles(f).len() > 1
|
||||
{
|
||||
let open = st.expanded.iter().any(|e| e.eq_ignore_ascii_case(f));
|
||||
p.text(tri.center(), egui::Align2::CENTER_CENTER, if open { "▾" } else { "▸" }, egui::FontId::proportional(11.0), t.text_dim);
|
||||
on_triangle = resp.interact_pointer_pos().is_some_and(|pos| tri.contains(pos));
|
||||
if resp.clicked() && on_triangle {
|
||||
toggled = Some(f.to_lowercase());
|
||||
}
|
||||
}
|
||||
// The star at the end of a family's row.
|
||||
let star = egui::Rect::from_min_max(egui::pos2(rect.max.x - STAR, rect.min.y), rect.max);
|
||||
let mut on_star = false;
|
||||
if let Row::Family(f) = row {
|
||||
let on = favorite(f);
|
||||
let hot = resp.hover_pos().is_some_and(|pos| star.contains(pos));
|
||||
if on || hot || st.highlight == Some(i) {
|
||||
let color = if on { t.accent } else { t.text_dim };
|
||||
p.text(star.center(), egui::Align2::CENTER_CENTER, if on { "★" } else { "☆" }, egui::FontId::proportional(12.0), color);
|
||||
}
|
||||
on_star = resp.interact_pointer_pos().is_some_and(|pos| star.contains(pos));
|
||||
if resp.clicked() && on_star {
|
||||
starred = Some(f.clone());
|
||||
}
|
||||
}
|
||||
let indent = if row.style().is_some() { DISCLOSURE + 12.0 } else { DISCLOSURE };
|
||||
let name_w = rect.width() * NAME_SHARE;
|
||||
let name_rect = egui::Rect::from_min_max(rect.min + vec2(indent, 0.0), egui::pos2(rect.min.x + name_w - 4.0, rect.max.y));
|
||||
// Family rows follow Show Font Names in English; style rows keep the style name (#394).
|
||||
let name = match row {
|
||||
Row::Style(_, style) => style.clone(),
|
||||
Row::Family(f) => db.family_display_name(f, look.english_names),
|
||||
Row::Heading(_) => continue,
|
||||
for i in range {
|
||||
let Some(row) = rows.get(i) else { continue };
|
||||
if let Row::Heading(label) = row {
|
||||
let (rect, _) = ui.allocate_exact_size(vec2(ui.available_width(), row_h), Sense::hover());
|
||||
// Existing translated menu labels; the heading cannot preview or apply a font.
|
||||
let label = match *label {
|
||||
"Recent Fonts" => tl!("Recent Fonts"),
|
||||
_ => tl!("Fonts"),
|
||||
};
|
||||
ui.painter_at(name_rect).text(name_rect.left_center(), egui::Align2::LEFT_CENTER, name, egui::FontId::proportional(12.0), t.text);
|
||||
let sample_rect = egui::Rect::from_min_max(rect.min + vec2(name_w, 2.0), rect.max - vec2(STAR + 2.0, 2.0));
|
||||
if !look.samples {
|
||||
} else if let Some(tex) = sample_texture(ui.ctx(), family, row.style(), sample, sample_rect.height()) {
|
||||
paint_sample(ui, sample_rect, &tex, t.text);
|
||||
}
|
||||
if resp.clicked() && !on_triangle && !on_star {
|
||||
chosen = row.pick();
|
||||
ui.painter_at(rect).text(
|
||||
rect.left_center() + vec2(4.0, 0.0),
|
||||
egui::Align2::LEFT_CENTER,
|
||||
label,
|
||||
egui::FontId::proportional(11.0),
|
||||
t.text_dim,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let Some(family) = row.family() else { continue };
|
||||
let (rect, resp) = ui.allocate_exact_size(vec2(ui.available_width(), row_h), Sense::click());
|
||||
if resp.hovered() && ui.input(|inp| inp.pointer.delta() != egui::Vec2::ZERO || inp.smooth_scroll_delta != egui::Vec2::ZERO) {
|
||||
hovered = Some(i);
|
||||
}
|
||||
let p = ui.painter_at(rect);
|
||||
if st.highlight == Some(i) {
|
||||
p.rect_filled(rect, 2.0, t.selection);
|
||||
} else if matches!(row, Row::Family(f) if f.eq_ignore_ascii_case(current)) {
|
||||
p.rect_filled(rect, 2.0, t.hover);
|
||||
}
|
||||
// The disclosure triangle of a family with several styles.
|
||||
let tri = egui::Rect::from_min_size(rect.min, vec2(DISCLOSURE, row_h));
|
||||
let mut on_triangle = false;
|
||||
if let Row::Family(f) = row
|
||||
&& db.styles(f).len() > 1
|
||||
{
|
||||
let open = st.expanded.iter().any(|e| e.eq_ignore_ascii_case(f));
|
||||
p.text(tri.center(), egui::Align2::CENTER_CENTER, if open { "▾" } else { "▸" }, egui::FontId::proportional(11.0), t.text_dim);
|
||||
on_triangle = resp.interact_pointer_pos().is_some_and(|pos| tri.contains(pos));
|
||||
if resp.clicked() && on_triangle {
|
||||
toggled = Some(f.to_lowercase());
|
||||
}
|
||||
}
|
||||
});
|
||||
st.scrolled = shown.state.clamped_offset().y;
|
||||
// The star at the end of a family's row.
|
||||
let star = egui::Rect::from_min_max(egui::pos2(rect.max.x - STAR, rect.min.y), rect.max);
|
||||
let mut on_star = false;
|
||||
if let Row::Family(f) = row {
|
||||
let on = favorite(f);
|
||||
let hot = resp.hover_pos().is_some_and(|pos| star.contains(pos));
|
||||
if on || hot || st.highlight == Some(i) {
|
||||
let color = if on { t.accent } else { t.text_dim };
|
||||
p.text(star.center(), egui::Align2::CENTER_CENTER, if on { "★" } else { "☆" }, egui::FontId::proportional(12.0), color);
|
||||
}
|
||||
on_star = resp.interact_pointer_pos().is_some_and(|pos| star.contains(pos));
|
||||
if resp.clicked() && on_star {
|
||||
starred = Some(f.clone());
|
||||
}
|
||||
}
|
||||
let indent = if row.style().is_some() { DISCLOSURE + 12.0 } else { DISCLOSURE };
|
||||
let name_w = rect.width() * NAME_SHARE;
|
||||
let name_rect = egui::Rect::from_min_max(rect.min + vec2(indent, 0.0), egui::pos2(rect.min.x + name_w - 4.0, rect.max.y));
|
||||
// Family rows follow Show Font Names in English; style rows keep the style name (#394).
|
||||
let name = match row {
|
||||
Row::Style(_, style) => style.clone(),
|
||||
Row::Family(f) => db.family_display_name(f, look.english_names),
|
||||
Row::Heading(_) => continue,
|
||||
};
|
||||
ui.painter_at(name_rect).text(name_rect.left_center(), egui::Align2::LEFT_CENTER, name, egui::FontId::proportional(12.0), t.text);
|
||||
let sample_rect = egui::Rect::from_min_max(rect.min + vec2(name_w, 2.0), rect.max - vec2(STAR + 2.0, 2.0));
|
||||
if !look.samples {
|
||||
} else if let Some(tex) = sample_texture(ui.ctx(), family, row.style(), sample, sample_rect.height()) {
|
||||
paint_sample(ui, sample_rect, &tex, t.text);
|
||||
}
|
||||
if resp.clicked() && !on_triangle && !on_star {
|
||||
chosen = row.pick();
|
||||
}
|
||||
}
|
||||
if let Some(f) = toggled {
|
||||
if st.expanded.contains(&f) {
|
||||
st.expanded.retain(|e| *e != f);
|
||||
|
||||
@@ -260,7 +260,7 @@ pub(crate) fn trace_button(app: &mut VectorcraftApp, ui: &mut Ui, width: f32) {
|
||||
/// The selected Image Trace object's preset (Control bar, Properties): choosing another traces it
|
||||
/// again with that preset.
|
||||
pub(crate) fn preset_dropdown(app: &mut VectorcraftApp, ui: &mut Ui, current: &str, width: f32) {
|
||||
if let Some(p) = widgets::combo(ui, "trace-preset", tl!(current), width, false, |ui| preset_list(app, ui, current)) {
|
||||
if let Some(p) = widgets::combo(ui, "trace-preset", tl!(current), width, |ui| preset_list(app, ui, current)) {
|
||||
trace_with(app, &p);
|
||||
}
|
||||
}
|
||||
@@ -436,7 +436,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
ui.horizontal(|ui| {
|
||||
widgets::dim_label(ui, tl!("Library:"));
|
||||
let key = st.params["library"].as_str().unwrap_or(DOCUMENT_SWATCHES).to_string();
|
||||
if let Some(k) = widgets::combo(ui, "it-library", &library_label(app, &key), 170.0, false, |ui| library_list(app, ui, &key)) {
|
||||
if let Some(k) = widgets::combo(ui, "it-library", &library_label(app, &key), 170.0, |ui| library_list(app, ui, &key)) {
|
||||
if k != DOCUMENT_SWATCHES {
|
||||
st.params["colorGroup"] = Value::Null;
|
||||
}
|
||||
@@ -451,7 +451,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
widgets::dim_label(ui, tl!("Colors:"));
|
||||
let group = color_group(app, &st.params).map(str::to_string);
|
||||
let label = group.clone().unwrap_or_else(|| tl!("All").to_string());
|
||||
if let Some(g) = widgets::combo(ui, "it-color-group", &label, 170.0, false, |ui| color_group_list(app, ui, group.as_deref())) {
|
||||
if let Some(g) = widgets::combo(ui, "it-color-group", &label, 170.0, |ui| color_group_list(app, ui, group.as_deref())) {
|
||||
st.params["colorGroup"] = json!(g);
|
||||
st.preset = "Custom".into();
|
||||
retrace = true;
|
||||
|
||||
@@ -525,7 +525,7 @@ fn artboard_size_controls_inner(
|
||||
let mut fit = None;
|
||||
ui.horizontal(|ui| {
|
||||
dim_label(ui, tl!("Preset:"));
|
||||
preset = widgets::combo(ui, "ab-preset", ¤t, 140.0, false, |ui| {
|
||||
preset = widgets::combo(ui, "ab-preset", ¤t, 140.0, |ui| {
|
||||
let mut chosen = None;
|
||||
if let Some(enabled) = fits {
|
||||
for (selection, label) in [(false, tl!("Fit to Artwork Bounds")), (true, tl!("Fit to Selected Art"))] {
|
||||
|
||||
@@ -395,6 +395,61 @@ fn the_search_field_stays_put_while_the_list_scrolls() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A search that matches nothing shrinks the menu to its search field and filters; clearing the
|
||||
/// search lists every family again, and the menu grows back to its height before the search
|
||||
/// (egui#5225). The search field stays where it was throughout.
|
||||
#[test]
|
||||
fn the_menu_grows_back_when_a_search_that_matched_nothing_is_cleared() {
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
let frame = |events: Vec<egui::Event>| {
|
||||
let input =
|
||||
egui::RawInput { events, screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(600.0, 800.0))), ..Default::default() };
|
||||
let mut at = egui::Pos2::ZERO;
|
||||
let mut out = ctx.run_ui(input, |ui| {
|
||||
at = ui.next_widget_position();
|
||||
font_menu(ui, "test-regrow", "Inter", 260.0, None, MenuLook { samples: false, ..Default::default() });
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
let text = |label: &str| {
|
||||
out.shapes.iter().find_map(|s| match &s.shape {
|
||||
egui::Shape::Text(t) if t.galley.text() == label => Some(t.pos),
|
||||
_ => None,
|
||||
})
|
||||
};
|
||||
// The filter row under the search field: it shows whatever the field holds.
|
||||
let filters = text("Any Kind");
|
||||
let menu = filters.and_then(|p| ctx.layer_id_at(p)).and_then(|l| ctx.memory(|m| m.area_rect(l.id)));
|
||||
(at, filters, text("Search"), text("No matching fonts").is_some(), menu.map(|r| r.height()))
|
||||
};
|
||||
let settle = |events: Vec<egui::Event>| {
|
||||
let mut last = frame(events);
|
||||
for _ in 0..5 {
|
||||
last = frame(vec![]);
|
||||
}
|
||||
last
|
||||
};
|
||||
let (at, ..) = frame(vec![]);
|
||||
let click = at + egui::vec2(40.0, 10.0);
|
||||
let button = |pressed| egui::Event::PointerButton { pos: click, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
frame(vec![egui::Event::PointerMoved(click), button(true)]);
|
||||
let (_, filters, search, _, full) = settle(vec![button(false)]);
|
||||
let (filters, search, full) = (filters.expect("the menu is open"), search.expect("the search field shows"), full.expect("the menu's area"));
|
||||
let nothing = "qqzzxxqqzz";
|
||||
let (_, f, _, none, small) = settle(vec![egui::Event::Text(nothing.into())]);
|
||||
assert!(none, "the search matches no family");
|
||||
assert_eq!(f, Some(filters), "the search field stays put while nothing matches");
|
||||
let small = small.expect("the menu stays open");
|
||||
assert!(small + MenuLook::default().row < full, "the menu shrinks to its search field: {small} of {full}");
|
||||
for _ in nothing.chars() {
|
||||
frame(key(egui::Key::Backspace));
|
||||
}
|
||||
let (_, f, s, none, grown) = settle(vec![]);
|
||||
assert!(!none, "every family is listed again");
|
||||
assert_eq!((f, s), (Some(filters), Some(search)), "the search field is where it was");
|
||||
assert_eq!(grown, Some(full), "the menu grows back to its height before the search");
|
||||
}
|
||||
|
||||
/// #563: a preview stays on the text while the menu stays on its family. The menu's caller passes
|
||||
/// the text's font, which the preview changed; the menu keeps the font from before the preview as
|
||||
/// the current one, so it doesn't take the previewed family for it and end the preview, then
|
||||
|
||||
@@ -344,7 +344,7 @@ fn escape_after_closing_a_popup_or_during_a_ruler_origin_drag_keeps_the_rotation
|
||||
let combo = combo.expect("the status bar's angle menu");
|
||||
w.click(0.0, egui::PointerButton::Primary, combo.rect.center());
|
||||
w.after(0.0, vec![]);
|
||||
assert!(egui::ComboBox::is_open(&w.ctx, combo.id), "the angle menu is open");
|
||||
assert!(craft_ui::ComboBox::is_open(&w.ctx, combo.id), "the angle menu is open");
|
||||
w.escape(0.2);
|
||||
assert!(!egui::Popup::is_any_open(&w.ctx), "Escape closes it");
|
||||
w.escape(0.2);
|
||||
|
||||
@@ -38,9 +38,9 @@ pub use color::{
|
||||
color_slider_named, color_slider_stepped, color_slider_valued, harmony_wheel, hex_field, hex_field_named, region_texture,
|
||||
};
|
||||
pub use drag::{PanelDrag, SwatchRows, art_drop, doc_preview, drag_source};
|
||||
pub(crate) use dropdowns::combo;
|
||||
#[cfg(test)]
|
||||
use dropdowns::combo_button_id;
|
||||
pub(crate) use dropdowns::{combo, combo_menu};
|
||||
pub use dropdowns::{
|
||||
dropdown, dropdown_named, dropdown_names, dropdown_names_named, dropdown_with, dropdown_with_named, list_scroll_style, menu_room, menu_scroll,
|
||||
panel_scroll, popover, set_menu_floor, strip_scroll,
|
||||
@@ -227,7 +227,7 @@ pub(crate) mod accessibility_tests {
|
||||
angle_dial(ui, "angle", 45.0, 28.0);
|
||||
channel_slider_named(ui, "channel", "H", 0.5, vec2(22.0, 80.0), &|_| Color32::GRAY);
|
||||
harmony_wheel(ui, "wheel", 120.0, &[red], Some(0));
|
||||
combo(ui, "font", "Regular", 120.0, true, |_| None::<usize>);
|
||||
combo_menu(ui, "font", "Regular", 120.0, |_| None::<usize>);
|
||||
});
|
||||
let slider = |label| {
|
||||
let n = named(&tree, label);
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn blend_separator_before(i: usize) -> bool {
|
||||
/// The blend-mode dropdown, its groups separated (Normal | darken | lighten | contrast | inversion
|
||||
/// | component modes). `None` (objects that differ) shows blank. Returns the chosen mode.
|
||||
pub fn blend_dropdown(ui: &mut Ui, id: impl std::hash::Hash, current: Option<BlendMode>, width: f32) -> Option<BlendMode> {
|
||||
combo(ui, id, current.map_or("", |m| tl!(m.label())), width, false, |ui| {
|
||||
combo(ui, id, current.map_or("", |m| tl!(m.label())), width, |ui| {
|
||||
let mut chosen = None;
|
||||
for (i, m) in BlendMode::ALL.into_iter().enumerate() {
|
||||
if blend_separator_before(i) {
|
||||
|
||||
@@ -21,7 +21,7 @@ pub fn dropdown_names(ui: &mut Ui, id: impl std::hash::Hash, current: &str, opti
|
||||
|
||||
/// [`dropdown_names`] for a row whose visible label is not an adjacent label widget.
|
||||
pub fn dropdown_names_named(ui: &mut Ui, id: impl std::hash::Hash, name: Option<&str>, current: &str, options: &[&str], width: f32) -> Option<usize> {
|
||||
combo_named(ui, id, name, current, width, false, |ui| {
|
||||
combo_named(ui, id, name, current, width, |ui| {
|
||||
let mut chosen = None;
|
||||
for (i, o) in options.iter().enumerate() {
|
||||
if ui.add(egui::Button::selectable(*o == current, *o)).clicked() {
|
||||
@@ -55,7 +55,7 @@ pub fn dropdown_with_named(
|
||||
width: f32,
|
||||
enabled: impl Fn(usize) -> bool,
|
||||
) -> Option<usize> {
|
||||
combo_named(ui, id, name, tl!(current), width, false, |ui| {
|
||||
combo_named(ui, id, name, tl!(current), width, |ui| {
|
||||
let mut chosen = None;
|
||||
for (i, o) in options.iter().enumerate() {
|
||||
if ui.add_enabled(enabled(i), egui::Button::selectable(*o == current, tl!(o))).clicked() {
|
||||
@@ -66,7 +66,7 @@ pub fn dropdown_with_named(
|
||||
})
|
||||
}
|
||||
|
||||
/// The id egui's [`egui::ComboBox::from_id_salt`] gives its button in this `Ui`: `from_id_salt`
|
||||
/// The id [`craft_ui::ComboBox::from_id_salt`] gives its button in this `Ui`: `from_id_salt`
|
||||
/// stores `IdSalt::new(salt)` and hashes it once more with the scope, so the app must hash `id`
|
||||
/// the same way to read the button's focus and open state before it is drawn.
|
||||
pub(super) fn combo_button_id(ui: &Ui, id: &impl std::hash::Hash) -> egui::Id {
|
||||
@@ -74,17 +74,9 @@ pub(super) fn combo_button_id(ui: &Ui, id: &impl std::hash::Hash) -> egui::Id {
|
||||
}
|
||||
|
||||
/// The recessed combo box of [`dropdown`] showing `current`; `list` draws the options and returns
|
||||
/// the chosen one. A `searchable` list (a search field over a list it scrolls itself) stays open
|
||||
/// while it is clicked: it closes itself when an option is chosen.
|
||||
pub(crate) fn combo<R>(
|
||||
ui: &mut Ui,
|
||||
id: impl std::hash::Hash,
|
||||
current: &str,
|
||||
width: f32,
|
||||
searchable: bool,
|
||||
list: impl FnOnce(&mut Ui) -> Option<R>,
|
||||
) -> Option<R> {
|
||||
combo_named(ui, id, None, current, width, searchable, list)
|
||||
/// the chosen one.
|
||||
pub(crate) fn combo<R>(ui: &mut Ui, id: impl std::hash::Hash, current: &str, width: f32, list: impl FnOnce(&mut Ui) -> Option<R>) -> Option<R> {
|
||||
combo_named(ui, id, None, current, width, list)
|
||||
}
|
||||
|
||||
/// [`combo`] for a row whose visible label is not an adjacent label widget.
|
||||
@@ -94,7 +86,6 @@ pub(crate) fn combo_named<R>(
|
||||
name: Option<&str>,
|
||||
current: &str,
|
||||
width: f32,
|
||||
searchable: bool,
|
||||
list: impl FnOnce(&mut Ui) -> Option<R>,
|
||||
) -> Option<R> {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
@@ -104,12 +95,9 @@ pub(crate) fn combo_named<R>(
|
||||
.stroke(Stroke::new(1.0, t.input_border))
|
||||
.corner_radius(CornerRadius::same(3))
|
||||
.show(ui, |ui| {
|
||||
if searchable {
|
||||
return searchable_combo(ui, id_with(ui.scope_id(), id), label, name, current, width - 4.0, list);
|
||||
}
|
||||
let button_id = combo_button_id(ui, &id);
|
||||
let enter_for_combo = ui.memory(|m| m.has_focus(button_id)) || egui::ComboBox::is_open(ui.ctx(), button_id);
|
||||
let response = egui::ComboBox::from_id_salt(HashOnly(&id))
|
||||
let enter_for_combo = ui.memory(|m| m.has_focus(button_id)) || craft_ui::ComboBox::is_open(ui.ctx(), button_id);
|
||||
let response = craft_ui::ComboBox::from_id_salt(HashOnly(&id))
|
||||
.selected_text(egui::RichText::new(current).size(12.0))
|
||||
.width(width - 4.0)
|
||||
.height(ui.spacing().combo_height)
|
||||
@@ -132,20 +120,35 @@ pub(crate) fn combo_named<R>(
|
||||
.inner
|
||||
}
|
||||
|
||||
/// A dropdown whose list keeps its own search field and scrolling rows (the font menu): drawn as
|
||||
/// egui's combo box, but in a popup without the scroll area egui puts around a combo's list, so
|
||||
/// the field stays at the top and only the rows scroll (#555). A click outside closes it.
|
||||
fn searchable_combo<R>(
|
||||
/// The recessed combo box of [`dropdown`] showing `current`, whose menu keeps a search field and
|
||||
/// filters in place over a list that scrolls (the font menu and the shortcut import's target
|
||||
/// picker, #555). `menu` shows the menu with one of [`craft_ui::combo::Menu`]'s header forms and
|
||||
/// returns what it returned (`None` while the menu is closed). A click inside the menu keeps it
|
||||
/// open: it closes itself when an option is chosen. A click outside closes it.
|
||||
pub(crate) fn combo_menu<R>(
|
||||
ui: &mut Ui,
|
||||
id: egui::Id,
|
||||
label: Option<PreviousLabel>,
|
||||
name: Option<&str>,
|
||||
id: impl std::hash::Hash,
|
||||
current: &str,
|
||||
width: f32,
|
||||
list: impl FnOnce(&mut Ui) -> Option<R>,
|
||||
menu: impl FnOnce(craft_ui::combo::Menu<'_>) -> Option<R>,
|
||||
) -> Option<R> {
|
||||
let popup = id.with("popup");
|
||||
let open = egui::Popup::is_id_open(ui.ctx(), popup);
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let label = take_previous_label(ui);
|
||||
egui::Frame::NONE
|
||||
.fill(t.input)
|
||||
.stroke(Stroke::new(1.0, t.input_border))
|
||||
.corner_radius(CornerRadius::same(3))
|
||||
.show(ui, |ui| {
|
||||
let resp = combo_field(ui, id_with(ui.scope_id(), id), label, None, current, width - 4.0);
|
||||
menu(craft_ui::combo::Menu::new(ui, &resp).close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside))
|
||||
})
|
||||
.inner
|
||||
}
|
||||
|
||||
/// The field of a searchable combo box, drawn as egui's combo box button, with the id `id`: its
|
||||
/// menu is the popup `id.with("popup")`.
|
||||
fn combo_field(ui: &mut Ui, id: egui::Id, label: Option<PreviousLabel>, name: Option<&str>, current: &str, width: f32) -> Response {
|
||||
let open = egui::Popup::is_id_open(ui.ctx(), id.with("popup"));
|
||||
let pad = ui.spacing().button_padding;
|
||||
let icon = Vec2::splat(ui.spacing().icon_width);
|
||||
let galley = egui::WidgetText::from(egui::RichText::new(current).size(12.0)).into_galley(
|
||||
@@ -155,7 +158,10 @@ fn searchable_combo<R>(
|
||||
egui::TextStyle::Button,
|
||||
);
|
||||
let height = (galley.size().y.max(icon.y) + 2.0 * pad.y).max(ui.spacing().interact_size.y);
|
||||
let (rect, resp) = ui.allocate_exact_size(vec2(width, height), Sense::click());
|
||||
let size = vec2(width, height);
|
||||
let (_, space) = ui.allocate_space(size);
|
||||
let rect = egui::Align2::CENTER_CENTER.align_size_within_rect(size, space);
|
||||
let resp = ui.interact(rect, id, Sense::click());
|
||||
// Screen readers get what egui's combo box gives them: its role and the text it shows.
|
||||
resp.widget_info(|| egui::WidgetInfo {
|
||||
enabled: ui.is_enabled(),
|
||||
@@ -177,26 +183,7 @@ fn searchable_combo<R>(
|
||||
Stroke::NONE,
|
||||
));
|
||||
}
|
||||
// It opens below the button, or above it when there is more room there, and the list gets
|
||||
// the room left (the popup's frame aside).
|
||||
const GAP: f32 = 12.0;
|
||||
let screen = ui.ctx().content_rect();
|
||||
let frame = ui.spacing().menu_margin.sum().y + GAP;
|
||||
let (below, above) = (screen.bottom() - rect.bottom() - frame, rect.top() - screen.top() - frame);
|
||||
let (align, room) = if below >= above { (egui::RectAlign::BOTTOM_START, below) } else { (egui::RectAlign::TOP_START, above) };
|
||||
egui::Popup::menu(&resp)
|
||||
.id(popup)
|
||||
.width(rect.width())
|
||||
.align(align)
|
||||
.align_alternatives(&[])
|
||||
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside)
|
||||
.show(|ui| {
|
||||
ui.set_min_width(ui.available_width());
|
||||
ui.set_max_height(room);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
list(ui)
|
||||
})
|
||||
.and_then(|r| r.inner)
|
||||
resp
|
||||
}
|
||||
|
||||
/// A popover anchored to `resp`, opened and closed by `toggle` (usually `resp.clicked()`), and
|
||||
|
||||
@@ -324,7 +324,7 @@ fn a_combos_keyboard_guard_reads_the_combos_own_button_id() {
|
||||
// `ComboBox::from_id_salt` gives the button: they must be equal, or the guard
|
||||
// reads and gives back the keyboard of nothing.
|
||||
let expected = combo_button_id(ui, &"combo-guard-test");
|
||||
let response = egui::ComboBox::from_id_salt("combo-guard-test").selected_text("One").show_ui(ui, |_| ());
|
||||
let response = craft_ui::ComboBox::from_id_salt("combo-guard-test").selected_text("One").show_ui(ui, |_| ());
|
||||
seen.set(expected == response.response.id);
|
||||
});
|
||||
h.run_steps(2);
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# PDF backend
|
||||
|
||||
PDF writing uses `craft-pdf` from `storytold/craft-libs`, pinned to
|
||||
`a0916808f7d78bed8c7b90cdf937176015454a7f`. Its `write` feature supplies Krilla configuration,
|
||||
`92d392cff132eaf53e13afff77f38b5c11b73aa0`. Its `write` feature supplies Krilla configuration,
|
||||
metadata construction, fallible font preparation and serialization, and native unwind guards.
|
||||
The dependency disables default features and enables `write` and `interpret`.
|
||||
The production writer retains Krilla’s raster-image support without its optional text shaper.
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::process::Command;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
const REV: &str = "a0916808f7d78bed8c7b90cdf937176015454a7f";
|
||||
const REV: &str = "92d392cff132eaf53e13afff77f38b5c11b73aa0";
|
||||
const PACKAGES: &[(&str, &str)] = &[
|
||||
("craft-pdf", "0.1.0"),
|
||||
("krilla", "0.8.2"),
|
||||
|
||||
Reference in New Issue
Block a user