Compare commits

..

No commits in common. "pre-1.9-merge-20260924" and "master" have entirely different histories.

113 changed files with 1189 additions and 3018 deletions

199
Cargo.lock generated
View file

@ -157,12 +157,6 @@ dependencies = [
"equator", "equator",
] ]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]] [[package]]
name = "android-activity" name = "android-activity"
version = "0.6.1" version = "0.6.1"
@ -787,13 +781,6 @@ dependencies = [
"cfg_aliases", "cfg_aliases",
] ]
[[package]]
name = "build_helpers"
version = "0.14.0"
dependencies = [
"cfg_aliases",
]
[[package]] [[package]]
name = "built" name = "built"
version = "0.8.0" version = "0.8.0"
@ -1301,7 +1288,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-client-toolkit" name = "cosmic-client-toolkit"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/pop-os/cosmic-protocols?rev=32283d7#32283d76a8d0342da74c4cc022a533c52dcf378f" source = "git+https://github.com/pop-os/cosmic-protocols?rev=160b086#160b086abe03cd34a8a375d7fbe47b24308d1f38"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"cosmic-protocols", "cosmic-protocols",
@ -1314,7 +1301,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-protocols" name = "cosmic-protocols"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/pop-os/cosmic-protocols?rev=32283d7#32283d76a8d0342da74c4cc022a533c52dcf378f" source = "git+https://github.com/pop-os/cosmic-protocols?rev=160b086#160b086abe03cd34a8a375d7fbe47b24308d1f38"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"wayland-backend", "wayland-backend",
@ -1327,9 +1314,8 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-text" name = "cosmic-text"
version = "0.19.0" version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/pop-os/cosmic-text.git#4d74f795cc771fdcc7ea0f9cacba63fcf036fad6"
checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"fontdb", "fontdb",
@ -1453,7 +1439,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]] [[package]]
name = "cryoglyph" name = "cryoglyph"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/iced-rs/cryoglyph.git?rev=e429a025df36ab8145708acb309080ae3deec17a#e429a025df36ab8145708acb309080ae3deec17a" source = "git+https://github.com/pop-os/glyphon.git?tag=cosmic-0.14#c49de15bce4d8254ac136d1be9911960cc85ce12"
dependencies = [ dependencies = [
"cosmic-text", "cosmic-text",
"etagere", "etagere",
@ -1575,7 +1561,7 @@ dependencies = [
"libc", "libc",
"option-ext", "option-ext",
"redox_users 0.5.2", "redox_users 0.5.2",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -1803,7 +1789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -2306,7 +2292,7 @@ dependencies = [
"gobject-sys", "gobject-sys",
"libc", "libc",
"system-deps", "system-deps",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -2418,17 +2404,34 @@ dependencies = [
] ]
[[package]] [[package]]
name = "gpu-allocator" name = "gpu-alloc"
version = "0.28.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
dependencies = [
"bitflags 2.11.0",
"gpu-alloc-types",
]
[[package]]
name = "gpu-alloc-types"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
dependencies = [
"bitflags 2.11.0",
]
[[package]]
name = "gpu-allocator"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"
dependencies = [ dependencies = [
"ash",
"hashbrown 0.16.1",
"log", "log",
"presser", "presser",
"thiserror 2.0.18", "thiserror 1.0.69",
"windows 0.57.0", "windows 0.58.0",
] ]
[[package]] [[package]]
@ -2696,8 +2699,6 @@ version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [ dependencies = [
"allocator-api2",
"equivalent",
"foldhash 0.2.0", "foldhash 0.2.0",
] ]
@ -2964,7 +2965,6 @@ dependencies = [
name = "iced" name = "iced"
version = "0.14.0" version = "0.14.0"
dependencies = [ dependencies = [
"build_helpers",
"criterion", "criterion",
"dnd", "dnd",
"iced_accessibility", "iced_accessibility",
@ -3016,7 +3016,6 @@ version = "0.14.0"
dependencies = [ dependencies = [
"approx", "approx",
"bitflags 2.11.0", "bitflags 2.11.0",
"build_helpers",
"bytes", "bytes",
"cosmic-client-toolkit", "cosmic-client-toolkit",
"dnd", "dnd",
@ -3032,7 +3031,6 @@ dependencies = [
"serde", "serde",
"smol_str", "smol_str",
"thiserror 2.0.18", "thiserror 2.0.18",
"unicode-segmentation",
"web-time", "web-time",
"window_clipboard", "window_clipboard",
] ]
@ -3123,7 +3121,6 @@ dependencies = [
name = "iced_runtime" name = "iced_runtime"
version = "0.14.0" version = "0.14.0"
dependencies = [ dependencies = [
"build_helpers",
"bytes", "bytes",
"cosmic-client-toolkit", "cosmic-client-toolkit",
"dnd", "dnd",
@ -3191,7 +3188,6 @@ version = "0.14.0"
dependencies = [ dependencies = [
"as-raw-xcb-connection", "as-raw-xcb-connection",
"bitflags 2.11.0", "bitflags 2.11.0",
"build_helpers",
"bytemuck", "bytemuck",
"cosmic-client-toolkit", "cosmic-client-toolkit",
"cryoglyph", "cryoglyph",
@ -3220,7 +3216,6 @@ dependencies = [
name = "iced_widget" name = "iced_widget"
version = "0.14.2" version = "0.14.2"
dependencies = [ dependencies = [
"build_helpers",
"cosmic-client-toolkit", "cosmic-client-toolkit",
"dnd", "dnd",
"iced_accessibility", "iced_accessibility",
@ -3242,7 +3237,6 @@ dependencies = [
name = "iced_winit" name = "iced_winit"
version = "0.14.0" version = "0.14.0"
dependencies = [ dependencies = [
"build_helpers",
"cosmic-client-toolkit", "cosmic-client-toolkit",
"cursor-icon", "cursor-icon",
"dnd", "dnd",
@ -3484,7 +3478,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -3542,7 +3536,7 @@ dependencies = [
"portable-atomic", "portable-atomic",
"portable-atomic-util", "portable-atomic-util",
"serde_core", "serde_core",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -4051,9 +4045,9 @@ dependencies = [
[[package]] [[package]]
name = "metal" name = "metal"
version = "0.33.0" version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"block", "block",
@ -4199,9 +4193,9 @@ checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af"
[[package]] [[package]]
name = "naga" name = "naga"
version = "28.0.0" version = "27.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bit-set", "bit-set",
@ -4306,7 +4300,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -5712,7 +5706,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.4.15", "linux-raw-sys 0.4.15",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -5725,7 +5719,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.12.1", "linux-raw-sys 0.12.1",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -6340,7 +6334,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -6634,7 +6628,7 @@ dependencies = [
"getrandom 0.4.2", "getrandom 0.4.2",
"once_cell", "once_cell",
"rustix 1.1.4", "rustix 1.1.4",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -7173,7 +7167,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [ dependencies = [
"memoffset", "memoffset",
"tempfile", "tempfile",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -7829,13 +7823,12 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]] [[package]]
name = "wgpu" name = "wgpu"
version = "28.0.0" version = "27.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bitflags 2.11.0", "bitflags 2.11.0",
"bytemuck",
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"document-features", "document-features",
@ -7859,9 +7852,9 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-core" name = "wgpu-core"
version = "28.0.1" version = "27.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d23f4642f53f666adcfd2d3218ab174d1e6681101aef18696b90cbe64d1c10f9" checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bit-set", "bit-set",
@ -7892,45 +7885,45 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-core-deps-apple" name = "wgpu-core-deps-apple"
version = "28.0.0" version = "27.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233"
dependencies = [ dependencies = [
"wgpu-hal", "wgpu-hal",
] ]
[[package]] [[package]]
name = "wgpu-core-deps-emscripten" name = "wgpu-core-deps-emscripten"
version = "28.0.0" version = "27.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5"
dependencies = [ dependencies = [
"wgpu-hal", "wgpu-hal",
] ]
[[package]] [[package]]
name = "wgpu-core-deps-wasm" name = "wgpu-core-deps-wasm"
version = "28.0.0" version = "27.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12a2cf578ce8d7d50d0e63ddc2345c7dcb599f6eb90b888813406ea78b9b7010" checksum = "9b1027dcf3b027a877e44819df7ceb0e2e98578830f8cd34cd6c3c7c2a7a50b7"
dependencies = [ dependencies = [
"wgpu-hal", "wgpu-hal",
] ]
[[package]] [[package]]
name = "wgpu-core-deps-windows-linux-android" name = "wgpu-core-deps-windows-linux-android"
version = "28.0.0" version = "27.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3"
dependencies = [ dependencies = [
"wgpu-hal", "wgpu-hal",
] ]
[[package]] [[package]]
name = "wgpu-hal" name = "wgpu-hal"
version = "28.0.1" version = "27.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce"
dependencies = [ dependencies = [
"android_system_properties", "android_system_properties",
"arrayvec", "arrayvec",
@ -7944,6 +7937,7 @@ dependencies = [
"core-graphics-types 0.2.0", "core-graphics-types 0.2.0",
"glow", "glow",
"glutin_wgl_sys", "glutin_wgl_sys",
"gpu-alloc",
"gpu-allocator", "gpu-allocator",
"gpu-descriptor", "gpu-descriptor",
"hashbrown 0.16.1", "hashbrown 0.16.1",
@ -7970,20 +7964,21 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
"wgpu-types", "wgpu-types",
"windows 0.62.2", "windows 0.58.0",
"windows-core 0.62.2", "windows-core 0.58.0",
] ]
[[package]] [[package]]
name = "wgpu-types" name = "wgpu-types"
version = "28.0.0" version = "27.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"bytemuck", "bytemuck",
"js-sys", "js-sys",
"log", "log",
"thiserror 2.0.18",
"web-sys", "web-sys",
] ]
@ -8009,7 +8004,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -8043,6 +8038,16 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core 0.58.0",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.61.3" version = "0.61.3"
@ -8098,6 +8103,19 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows-core"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement 0.58.0",
"windows-interface 0.58.0",
"windows-result 0.2.0",
"windows-strings 0.1.0",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.61.2" version = "0.61.2"
@ -8157,6 +8175,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "windows-implement" name = "windows-implement"
version = "0.60.2" version = "0.60.2"
@ -8179,6 +8208,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "windows-interface" name = "windows-interface"
version = "0.59.3" version = "0.59.3"
@ -8242,6 +8282,15 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.3.4" version = "0.3.4"
@ -8260,6 +8309,16 @@ dependencies = [
"windows-link 0.2.1", "windows-link 0.2.1",
] ]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result 0.2.0",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-strings" name = "windows-strings"
version = "0.4.2" version = "0.4.2"

View file

@ -118,17 +118,13 @@ winit = [
"iced_accessibility?/accesskit_winit", "iced_accessibility?/accesskit_winit",
"iced_program/winit", "iced_program/winit",
] ]
wayland = [ wayland = ["iced_renderer/wayland", "iced_winit/wayland"]
"iced_renderer/wayland", cctk = ["iced_winit/cctk", "iced_widget/cctk", "iced_core/cctk", "wayland"]
"iced_winit/wayland", # Enables the sctk shell.
"iced_runtime/wayland",
"iced_widget/wayland",
"iced_core/wayland",
]
[dependencies] [dependencies]
iced_devtools.workspace = true iced_devtools.workspace = true
iced_devtools.optional = true
iced_debug.workspace = true iced_debug.workspace = true
iced_program.workspace = true iced_program.workspace = true
@ -138,9 +134,10 @@ iced_renderer.workspace = true
iced_runtime.workspace = true iced_runtime.workspace = true
iced_widget.workspace = true iced_widget.workspace = true
iced_winit.workspace = true iced_winit.workspace = true
iced_winit.optional = true iced_devtools.optional = true
iced_tester.workspace = true iced_tester.workspace = true
iced_tester.optional = true iced_tester.optional = true
iced_winit.optional = true
iced_highlighter.workspace = true iced_highlighter.workspace = true
iced_highlighter.optional = true iced_highlighter.optional = true
iced_accessibility.workspace = true iced_accessibility.workspace = true
@ -193,7 +190,6 @@ members = [
"winit", "winit",
"examples/*", "examples/*",
"accessibility", "accessibility",
"build_helpers",
] ]
exclude = ["examples/integration"] exclude = ["examples/integration"]
@ -206,7 +202,7 @@ repository = "https://github.com/iced-rs/iced"
homepage = "https://iced.rs" homepage = "https://iced.rs"
categories = ["gui"] categories = ["gui"]
keywords = ["gui", "ui", "graphics", "interface", "widgets"] keywords = ["gui", "ui", "graphics", "interface", "widgets"]
rust-version = "1.92" rust-version = "1.89"
[workspace.dependencies] [workspace.dependencies]
iced = { version = "0.14.0", path = "." } iced = { version = "0.14.0", path = "." }
@ -242,9 +238,10 @@ async-std = "1.0"
bitflags = "2.5" bitflags = "2.5"
bytemuck = { version = "1.0", features = ["derive"] } bytemuck = { version = "1.0", features = ["derive"] }
bytes = "1.6" bytes = "1.6"
cosmic-text = { path = "../../cosmic-text" } cosmic-text = { git = "https://github.com/pop-os/cosmic-text.git" }
cryoglyph = { package = "cryoglyph", path = "../../glyphon" } # cosmic-text = "0.10"
dark-light = "1.0" dark-light = "1.0"
cryoglyph = { package = "cryoglyph", git = "https://github.com/pop-os/glyphon.git", tag = "cosmic-0.14" }
resvg = "0.45" resvg = "0.45"
web-sys = "0.3.69" web-sys = "0.3.69"
guillotiere = "0.6" guillotiere = "0.6"
@ -278,11 +275,8 @@ tiny-skia = { version = "0.11", default-features = false, features = [
"std", "std",
"simd", "simd",
] } ] }
cctk = { path = "../../cosmic-protocols/client-toolkit", package = "cosmic-client-toolkit" } cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "160b086" }
# Yoda: default-features=false drops softbuffer's x11 / x11-dlopen (pulled softbuffer = { git = "https://github.com/pop-os/softbuffer", tag = "cosmic-4.0" }
# tiny-xlib + as-raw-xcb-connection). The wayland + wayland-dlopen features
# are re-activated via iced_tiny_skia's own `wayland` feature propagation.
softbuffer = { path = "../../softbuffer", default-features = false }
syntect = "5.2" syntect = "5.2"
tokio = "1.0" tokio = "1.0"
tracing = "0.1" tracing = "0.1"
@ -294,7 +288,7 @@ url = "2.5"
wasm-bindgen-futures = "0.4" wasm-bindgen-futures = "0.4"
wasmtimer = "0.4.2" wasmtimer = "0.4.2"
web-time = "1.1" web-time = "1.1"
wgpu = { version = "28.0", default-features = false, features = [ wgpu = { version = "27.0", default-features = false, features = [
"std", "std",
"wgsl", "wgsl",
] } ] }
@ -308,40 +302,13 @@ wayland-client = { version = "0.31.5" }
winapi = "0.3" winapi = "0.3"
# window_clipboard = "0.4.1" # window_clipboard = "0.4.1"
# Yoda: window_clipboard pinned to our public fork that has x11 gated behind window_clipboard = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" }
# an opt-in feature. default-features=false + features=["wayland"] drops dnd = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" }
# clipboard_x11 + x11rb from the build. Branch yoda-x11-optional on mime = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" }
# https://forge.aditua.com/leyoda/window_clipboard. winit = { git = "https://github.com/pop-os/winit.git", tag = "cosmic-0.14" }
window_clipboard = { path = "../../window_clipboard", default-features = false, features = ["wayland"] } winit-core = { git = "https://github.com/pop-os/winit.git", tag = "cosmic-0.14" }
dnd = { path = "../../window_clipboard/dnd" }
mime = { path = "../../window_clipboard/mime" }
# Yoda: default-features = false drops winit's x11 default, which otherwise
# pulls winit-x11, x11-dl, tiny-xlib, as-raw-xcb-connection. Our Wayland-only
# fork doesn't need any of that — the wayland + wayland-dlopen features are
# re-enabled via iced_winit's own feature propagation from libcosmic-yoda.
winit = { path = "../../winit/winit", default-features = false }
winit-core = { path = "../../winit/winit-core" }
cursor-icon = "1.1.0" cursor-icon = "1.1.0"
[patch.'https://github.com/pop-os/softbuffer']
softbuffer = { path = "../../softbuffer" }
[patch.'https://github.com/pop-os/smithay-clipboard']
smithay-clipboard = { path = "../../smithay-clipboard" }
[patch.'https://github.com/pop-os/winit.git']
dpi = { path = "../../winit/dpi" }
winit = { path = "../../winit/winit" }
winit-android = { path = "../../winit/winit-android" }
winit-appkit = { path = "../../winit/winit-appkit" }
winit-common = { path = "../../winit/winit-common" }
winit-core = { path = "../../winit/winit-core" }
winit-orbital = { path = "../../winit/winit-orbital" }
winit-uikit = { path = "../../winit/winit-uikit" }
winit-wayland = { path = "../../winit/winit-wayland" }
winit-web = { path = "../../winit/winit-web" }
winit-win32 = { path = "../../winit/winit-win32" }
winit-x11 = { path = "../../winit/winit-x11" }
[workspace.lints.rust] [workspace.lints.rust]
rust_2018_idioms = { level = "deny", priority = -1 } rust_2018_idioms = { level = "deny", priority = -1 }
@ -367,6 +334,3 @@ useless_conversion = "deny"
[workspace.lints.rustdoc] [workspace.lints.rustdoc]
broken_intra_doc_links = "forbid" broken_intra_doc_links = "forbid"
[build-dependencies]
build_helpers = { path = "./build_helpers" }

View file

@ -1,3 +0,0 @@
fn main() {
build_helpers::cfg_aliases_setup();
}

View file

@ -1,17 +0,0 @@
[package]
name = "build_helpers"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
categories.workspace = true
keywords.workspace = true
rust-version.workspace = true
[dependencies]
cfg_aliases = { version = "0.2.1" }
[lints]
workspace = true

View file

@ -1,24 +0,0 @@
use cfg_aliases::cfg_aliases;
pub fn cfg_aliases_setup() {
// The script doesn't depend on our code
println!("cargo:rerun-if-changed=build.rs");
// Setup cfg aliases
cfg_aliases! {
// Systems.
android_platform: { target_os = "android" },
wasm_platform: { all(target_family = "wasm", not(target_os = "emscripten")) },
macos_platform: { target_os = "macos" },
ios_platform: { target_os = "ios" },
windows_platform: { target_os = "windows" },
apple: { any(target_os = "ios", target_os = "macos") },
free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) },
redox: { target_os = "redox" },
// Native displays.
wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
xdg_portal: { all(feature = "xdg-portal", free_unix, not(wasm), not(redox)) },
orbital_platform: { redox },
}
}

View file

@ -19,7 +19,6 @@ crisp = []
basic-shaping = [] basic-shaping = []
advanced-shaping = [] advanced-shaping = []
a11y = ["iced_accessibility"] a11y = ["iced_accessibility"]
wayland = ["cctk"]
[dependencies] [dependencies]
palette = "0.7" palette = "0.7"
@ -32,7 +31,6 @@ num-traits.workspace = true
rustc-hash.workspace = true rustc-hash.workspace = true
smol_str.workspace = true smol_str.workspace = true
thiserror.workspace = true thiserror.workspace = true
unicode-segmentation.workspace = true
web-time.workspace = true web-time.workspace = true
serde.workspace = true serde.workspace = true
@ -44,6 +42,8 @@ window_clipboard.workspace = true
dnd.workspace = true dnd.workspace = true
mime.workspace = true mime.workspace = true
cctk.workspace = true
cctk.optional = true
# /TODO(POP) # /TODO(POP)
[dependencies.iced_accessibility] [dependencies.iced_accessibility]
version = "0.1.0" version = "0.1.0"
@ -55,10 +55,3 @@ raw-window-handle.workspace = true
[dev-dependencies] [dev-dependencies]
approx = "0.5" approx = "0.5"
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
cctk.workspace = true
cctk.optional = true
[build-dependencies]
build_helpers = { path = "../build_helpers" }

View file

@ -1,3 +0,0 @@
fn main() {
build_helpers::cfg_aliases_setup();
}

View file

@ -1,4 +1,4 @@
use crate::event::Event; use crate::event::{self, Event};
use crate::id::Id; use crate::id::Id;
use crate::layout; use crate::layout;
use crate::mouse; use crate::mouse;

View file

@ -7,8 +7,7 @@ use crate::keyboard;
use crate::mouse; use crate::mouse;
use crate::touch; use crate::touch;
use crate::window; use crate::window;
#[cfg(all(feature = "cctk", target_os = "linux"))]
#[cfg(wayland_platform)]
/// A platform specific event for wayland /// A platform specific event for wayland
pub mod wayland; pub mod wayland;
/// A user interface event. /// A user interface event.
@ -50,7 +49,7 @@ pub enum Event {
/// A platform specific event /// A platform specific event
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum PlatformSpecific { pub enum PlatformSpecific {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
/// A Wayland specific event /// A Wayland specific event
Wayland(wayland::Event), Wayland(wayland::Event),
} }

View file

@ -46,6 +46,4 @@ pub enum Event {
Subsurface(SubsurfaceEvent), Subsurface(SubsurfaceEvent),
/// Keyboard inhibit shortcuts /// Keyboard inhibit shortcuts
ShortcutsInhibited(bool), ShortcutsInhibited(bool),
/// Blur Support Enabled
BlurEnabled,
} }

View file

@ -3,7 +3,7 @@ use crate::mouse;
use crate::overlay; use crate::overlay;
use crate::renderer; use crate::renderer;
use crate::widget; use crate::widget;
use crate::{Clipboard, Event, Layout, Overlay, Shell, Size}; use crate::{Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size};
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`] /// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
/// children. /// children.

View file

@ -78,8 +78,6 @@ pub enum Action {
SelectLine, SelectLine,
/// Select the entire buffer. /// Select the entire buffer.
SelectAll, SelectAll,
/// Clear the selection
ClearSelection,
/// Perform an [`Edit`]. /// Perform an [`Edit`].
Edit(Edit), Edit(Edit),
/// Click the [`Editor`] at the given [`Point`]. /// Click the [`Editor`] at the given [`Point`].

View file

@ -4,7 +4,7 @@ use super::Operation;
use crate::{ use crate::{
Rectangle, Rectangle,
id::Id, id::Id,
widget::operation::Outcome, widget::operation::{Outcome, focusable::Count},
}; };
/// Produces an [`Operation`] that searches for the Id /// Produces an [`Operation`] that searches for the Id

View file

@ -22,18 +22,16 @@
//! ``` //! ```
use crate::alignment; use crate::alignment;
use crate::layout; use crate::layout;
use crate::mouse::{self, click}; use crate::mouse;
use crate::renderer; use crate::renderer;
use crate::text;
use crate::text::paragraph::{self, Paragraph}; use crate::text::paragraph::{self, Paragraph};
use crate::text::{self, Fragment};
use crate::widget::tree::{self, Tree}; use crate::widget::tree::{self, Tree};
use crate::{ use crate::{
Clipboard, Color, Element, Event, Layout, Length, Pixels, Point, Rectangle, Color, Element, Layout, Length, Pixels, Rectangle, Size, Theme, Widget,
Shell, Size, Theme, Widget, keyboard, touch,
}; };
use unicode_segmentation::UnicodeSegmentation; use std::borrow::Cow;
pub use text::{Alignment, Ellipsize, LineHeight, Shaping, Wrapping}; pub use text::{Alignment, Ellipsize, LineHeight, Shaping, Wrapping};
/// A bunch of text. /// A bunch of text.
@ -67,7 +65,6 @@ where
fragment: text::Fragment<'a>, fragment: text::Fragment<'a>,
format: Format<Renderer::Font>, format: Format<Renderer::Font>,
class: Theme::Class<'a>, class: Theme::Class<'a>,
selectable: bool,
} }
impl<'a, Theme, Renderer> Text<'a, Theme, Renderer> impl<'a, Theme, Renderer> Text<'a, Theme, Renderer>
@ -82,7 +79,6 @@ where
fragment: fragment.into_fragment(), fragment: fragment.into_fragment(),
format: Format::default(), format: Format::default(),
class: Theme::default(), class: Theme::default(),
selectable: false,
} }
} }
@ -193,17 +189,7 @@ where
{ {
let color = color.map(Into::into); let color = color.map(Into::into);
self.style(move |_theme| Style { self.style(move |_theme| Style { color })
color,
..Style::default()
})
}
/// Makes the [`Text`] selectable. When enabled, the user can click and
/// drag to select text, and copy it with Ctrl+C / Cmd+C.
pub fn selectable(mut self) -> Self {
self.selectable = true;
self
} }
/// Sets the style class of the [`Text`]. /// Sets the style class of the [`Text`].
@ -216,114 +202,7 @@ where
} }
/// The internal state of a [`Text`] widget. /// The internal state of a [`Text`] widget.
pub struct State<P: Paragraph> { pub type State<P> = paragraph::Plain<P>;
/// The cached paragraph layout.
pub paragraph: paragraph::Plain<P>,
/// Lazily allocated when text is selectable and first interacted with.
selection: Option<Box<SelectionState>>,
focused: bool,
keyboard_focused: bool,
context_menu_position: Option<Point>,
}
impl<P: Paragraph> Default for State<P> {
fn default() -> Self {
Self {
paragraph: paragraph::Plain::default(),
selection: None,
focused: false,
keyboard_focused: false,
context_menu_position: None,
}
}
}
impl<P: Paragraph> std::fmt::Debug for State<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("State")
.field("selection", &self.selection)
.field("focused", &self.focused)
.finish_non_exhaustive()
}
}
impl<P: Paragraph> State<P> {
/// Returns `true` if the widget currently has focus.
pub fn is_focused(&self) -> bool {
self.focused
}
/// Returns `true` if focus was gained via keyboard navigation (Tab).
pub fn is_keyboard_focused(&self) -> bool {
self.keyboard_focused
}
/// Clears focus, selection, and all interaction state.
pub fn clear_focus(&mut self) {
self.focused = false;
self.keyboard_focused = false;
self.context_menu_position = None;
if let Some(sel) = &mut self.selection {
sel.clear();
}
}
/// Returns the context menu position, if a context menu should be shown.
pub fn context_menu_position(&self) -> Option<Point> {
self.context_menu_position
}
/// Sets or clears the context menu position.
pub fn set_context_menu_position(&mut self, pos: Option<Point>) {
self.context_menu_position = pos;
}
}
impl<P: Paragraph> std::ops::Deref for State<P> {
type Target = paragraph::Plain<P>;
fn deref(&self) -> &paragraph::Plain<P> {
&self.paragraph
}
}
impl<P: Paragraph> std::ops::DerefMut for State<P> {
fn deref_mut(&mut self) -> &mut paragraph::Plain<P> {
&mut self.paragraph
}
}
#[derive(Debug, Clone, Default)]
struct SelectionState {
anchor: usize,
end: usize,
dragging: bool,
modifiers: keyboard::Modifiers,
last_click: Option<click::Click>,
}
impl SelectionState {
fn clear(&mut self) {
self.anchor = 0;
self.end = 0;
self.dragging = false;
}
}
impl<P: Paragraph> crate::widget::operation::Focusable for State<P> {
fn is_focused(&self) -> bool {
self.focused
}
fn focus(&mut self) {
self.focused = true;
self.keyboard_focused = true;
}
fn unfocus(&mut self) {
self.clear_focus();
}
}
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Text<'_, Theme, Renderer> for Text<'_, Theme, Renderer>
@ -336,7 +215,7 @@ where
} }
fn state(&self) -> tree::State { fn state(&self) -> tree::State {
tree::State::new(State::<Renderer::Paragraph>::default()) tree::State::new(paragraph::Plain::<Renderer::Paragraph>::default())
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {
@ -352,10 +231,8 @@ where
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
layout( layout(
&mut state.paragraph, tree.state.downcast_mut::<State<Renderer::Paragraph>>(),
renderer, renderer,
limits, limits,
&self.fragment, &self.fragment,
@ -375,351 +252,25 @@ where
) { ) {
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>(); let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
let style = theme.style(&self.class); let style = theme.style(&self.class);
let bounds = layout.bounds();
let paragraph = state.paragraph.raw();
if let Some(sel) = &state.selection {
let left = sel.anchor.min(sel.end);
let right = sel.anchor.max(sel.end);
let content: &str = self.fragment.as_ref();
if left != right { draw(
let lo_byte = grapheme_to_byte(content, left); renderer,
let hi_byte = grapheme_to_byte(content, right); defaults,
layout.bounds(),
let anchor = bounds.anchor( state.raw(),
paragraph.min_bounds(), style,
paragraph.align_x(), viewport,
paragraph.align_y(),
); );
let rects = paragraph.highlight(
0,
(lo_byte, text::Affinity::After),
(hi_byte, text::Affinity::Before),
);
for r in rects {
renderer.fill_quad(
renderer::Quad {
bounds: Rectangle {
x: anchor.x + r.x,
y: anchor.y + r.y,
width: r.width,
height: r.height,
},
..renderer::Quad::default()
},
style.selected_fill,
);
}
}
}
draw(renderer, defaults, bounds, paragraph, style, viewport);
}
fn update(
&mut self,
tree: &mut Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
_renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
_viewport: &Rectangle,
) {
if !self.selectable {
return;
}
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
let bounds = layout.bounds();
let content: &str = self.fragment.as_ref();
let grapheme_count = content.graphemes(true).count();
let paragraph = state.paragraph.raw();
// Any click outside this widget clears selection and focus.
if matches!(
event,
Event::Mouse(mouse::Event::ButtonPressed(_))
| Event::Touch(touch::Event::FingerPressed { .. })
) {
if cursor.position_over(bounds).is_none() {
let was_visible = state.focused
|| state.keyboard_focused
|| state
.selection
.as_ref()
.is_some_and(|sel| sel.anchor != sel.end);
state.focused = false;
state.keyboard_focused = false;
if let Some(sel) = &mut state.selection {
sel.clear();
}
if was_visible {
shell.request_redraw();
}
}
}
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
| Event::Touch(touch::Event::FingerPressed { .. }) => {
if state.context_menu_position.take().is_some() {
shell.capture_event();
return;
}
if let Some(pos) = cursor.position_over(bounds) {
let sel = state.selection.get_or_insert_with(|| {
Box::new(SelectionState::default())
});
let anchor = bounds.anchor(
paragraph.min_bounds(),
paragraph.align_x(),
paragraph.align_y(),
);
let relative =
Point::new(pos.x - anchor.x, pos.y - anchor.y);
let grapheme_pos =
hit_to_grapheme(paragraph, relative, content);
let new_click = click::Click::new(
pos,
mouse::Button::Left,
sel.last_click.take(),
);
match new_click.kind() {
click::Kind::Single => {
if sel.modifiers.shift() {
sel.end = grapheme_pos;
} else {
sel.anchor = grapheme_pos;
sel.end = grapheme_pos;
}
sel.dragging = true;
}
click::Kind::Double => {
sel.anchor =
previous_start_of_word(content, grapheme_pos);
sel.end = next_end_of_word(content, grapheme_pos);
sel.dragging = true;
}
click::Kind::Triple => {
sel.anchor = 0;
sel.end = grapheme_count;
sel.dragging = true;
}
}
sel.last_click = Some(new_click);
state.focused = true;
state.keyboard_focused = false;
shell.capture_event();
shell.request_redraw();
}
}
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => {
if let Some(pos) = cursor.position_over(bounds) {
state.context_menu_position = Some(pos);
state.focused = true;
state.keyboard_focused = false;
shell.capture_event();
shell.request_redraw();
}
}
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
| Event::Touch(touch::Event::FingerLifted { .. }) => {
if let Some(sel) = &mut state.selection {
sel.dragging = false;
}
}
Event::Mouse(mouse::Event::CursorMoved { position })
| Event::Touch(touch::Event::FingerMoved { position, .. }) => {
if let Some(sel) = &mut state.selection {
if sel.dragging {
let anchor = bounds.anchor(
paragraph.min_bounds(),
paragraph.align_x(),
paragraph.align_y(),
);
let relative = Point::new(
position.x - anchor.x,
position.y - anchor.y,
);
let end = hit_to_grapheme(paragraph, relative, content);
// Only the drag reaching a new grapheme changes what
// is drawn; plain motion inside one does not.
if end != sel.end {
sel.end = end;
shell.request_redraw();
}
shell.capture_event();
}
}
}
Event::Keyboard(keyboard::Event::KeyPressed {
key,
modifiers,
physical_key,
text: _,
..
}) => {
if !state.focused {
return;
}
let sel = state
.selection
.get_or_insert_with(|| Box::new(SelectionState::default()));
if modifiers.command() {
match key.to_latin(*physical_key) {
Some('c') => {
let left = sel.anchor.min(sel.end);
let right = sel.anchor.max(sel.end);
if left != right {
let selected: String = content
.graphemes(true)
.skip(left)
.take(right - left)
.collect();
clipboard.write(
crate::clipboard::Kind::Standard,
selected,
);
}
shell.capture_event();
return;
}
Some('a') => {
sel.anchor = 0;
sel.end = grapheme_count;
shell.capture_event();
shell.request_redraw();
return;
}
_ => {}
}
}
match key {
keyboard::Key::Named(keyboard::key::Named::ArrowLeft) => {
let by_word = is_jump_modifier(*modifiers);
if modifiers.shift() {
sel.end = if by_word {
previous_start_of_word(content, sel.end)
} else {
sel.end.saturating_sub(1)
};
} else {
let left = sel.anchor.min(sel.end);
let pos = if by_word {
previous_start_of_word(content, left)
} else {
left.saturating_sub(1)
};
sel.anchor = pos;
sel.end = pos;
}
shell.capture_event();
shell.request_redraw();
}
keyboard::Key::Named(keyboard::key::Named::ArrowRight) => {
let by_word = is_jump_modifier(*modifiers);
if modifiers.shift() {
sel.end = if by_word {
next_end_of_word(content, sel.end)
} else {
(sel.end + 1).min(grapheme_count)
};
} else {
let right = sel.anchor.max(sel.end);
let pos = if by_word {
next_end_of_word(content, right)
} else {
(right + 1).min(grapheme_count)
};
sel.anchor = pos;
sel.end = pos;
}
shell.capture_event();
shell.request_redraw();
}
keyboard::Key::Named(keyboard::key::Named::Home) => {
if modifiers.shift() {
sel.end = 0;
} else {
sel.anchor = 0;
sel.end = 0;
}
shell.capture_event();
shell.request_redraw();
}
keyboard::Key::Named(keyboard::key::Named::End) => {
if modifiers.shift() {
sel.end = grapheme_count;
} else {
sel.anchor = grapheme_count;
sel.end = grapheme_count;
}
shell.capture_event();
shell.request_redraw();
}
_ => {}
}
}
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
if let Some(sel) = &mut state.selection {
sel.modifiers = *modifiers;
}
}
_ => {}
}
}
fn mouse_interaction(
&self,
_tree: &Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
if self.selectable && cursor.is_over(layout.bounds()) {
mouse::Interaction::Text
} else {
mouse::Interaction::None
}
} }
fn operate( fn operate(
&mut self, &mut self,
tree: &mut Tree, _tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
_renderer: &Renderer, _renderer: &Renderer,
operation: &mut dyn super::Operation, operation: &mut dyn super::Operation,
) { ) {
operation.text(None, layout.bounds(), &self.fragment); operation.text(None, layout.bounds(), &self.fragment);
if self.selectable {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
operation.focusable(None, layout.bounds(), state);
}
} }
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
@ -920,23 +471,12 @@ where
} }
/// The appearance of some text. /// The appearance of some text.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Style { pub struct Style {
/// The [`Color`] of the text. /// The [`Color`] of the text.
/// ///
/// The default, `None`, means using the inherited color. /// The default, `None`, means using the inherited color.
pub color: Option<Color>, pub color: Option<Color>,
/// The fill [`Color`] of the selection highlight.
pub selected_fill: Color,
}
impl Default for Style {
fn default() -> Self {
Self {
color: None,
selected_fill: DEFAULT_SELECTION_COLOR,
}
}
} }
/// The theme catalog of a [`Text`]. /// The theme catalog of a [`Text`].
@ -970,14 +510,13 @@ impl Catalog for Theme {
/// The default text styling; color is inherited. /// The default text styling; color is inherited.
pub fn default(_theme: &Theme) -> Style { pub fn default(_theme: &Theme) -> Style {
Style::default() Style { color: None }
} }
/// Text with the default base color. /// Text with the default base color.
pub fn base(theme: &Theme) -> Style { pub fn base(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().text), color: Some(theme.palette().text),
..Style::default()
} }
} }
@ -985,7 +524,6 @@ pub fn base(theme: &Theme) -> Style {
pub fn primary(theme: &Theme) -> Style { pub fn primary(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().primary), color: Some(theme.palette().primary),
..Style::default()
} }
} }
@ -993,7 +531,6 @@ pub fn primary(theme: &Theme) -> Style {
pub fn secondary(theme: &Theme) -> Style { pub fn secondary(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.extended_palette().secondary.base.color), color: Some(theme.extended_palette().secondary.base.color),
..Style::default()
} }
} }
@ -1001,7 +538,6 @@ pub fn secondary(theme: &Theme) -> Style {
pub fn success(theme: &Theme) -> Style { pub fn success(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().success), color: Some(theme.palette().success),
..Style::default()
} }
} }
@ -1009,7 +545,6 @@ pub fn success(theme: &Theme) -> Style {
pub fn warning(theme: &Theme) -> Style { pub fn warning(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().warning), color: Some(theme.palette().warning),
..Style::default()
} }
} }
@ -1017,177 +552,5 @@ pub fn warning(theme: &Theme) -> Style {
pub fn danger(theme: &Theme) -> Style { pub fn danger(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().danger), color: Some(theme.palette().danger),
..Style::default()
}
}
use crate::widget::tree::Tree as WidgetTree;
/// Implement this on a **widget** to enable context menu support for
/// text selection (Copy, Select All, and optionally Cut / Paste) in libcosmic
pub trait HasSelectableText {
/// Returns the currently selected text, if any.
fn selected_text(&self, tree: &WidgetTree) -> Option<String>;
/// Selects all text.
fn select_all(&self, tree: &mut WidgetTree);
/// Returns `true` if the widget is editable (enables Cut / Paste).
fn is_editable(&self) -> bool {
false
}
/// Returns `true` if the widget is currently focused.
fn is_focused(&self, tree: &WidgetTree) -> bool;
/// Returns the position where the context menu should appear
fn context_menu_position(&self, tree: &WidgetTree) -> Option<Point>;
/// Sets or clears the context menu position.
fn set_context_menu_position(
&self,
tree: &mut WidgetTree,
pos: Option<Point>,
);
/// Copies the selection to the clipboard.
fn copy_to_clipboard(
&self,
tree: &WidgetTree,
clipboard: &mut dyn Clipboard,
) {
if let Some(text) = self.selected_text(tree) {
clipboard.write(crate::clipboard::Kind::Standard, text);
}
}
/// Deletes the selected text and returns the new full content.
/// Only called when [`is_editable`](Self::is_editable) is `true`.
fn delete_selection(&self, _tree: &mut WidgetTree) -> Option<String> {
None
}
/// Inserts `text` at the cursor (replacing any selection) and returns
/// the new full content.
/// Only called when [`is_editable`](Self::is_editable) is `true`.
fn paste_text(
&self,
_tree: &mut WidgetTree,
_text: &str,
) -> Option<String> {
None
}
}
impl<Theme: Catalog, Renderer: text::Renderer> HasSelectableText
for Text<'_, Theme, Renderer>
{
fn selected_text(&self, tree: &WidgetTree) -> Option<String> {
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
let sel = state.selection.as_ref()?;
let left = sel.anchor.min(sel.end);
let right = sel.anchor.max(sel.end);
if left == right {
return None;
}
let content = state.paragraph.content();
let lo = grapheme_to_byte(content, left);
let hi = grapheme_to_byte(content, right);
content.get(lo..hi).map(|s| s.to_owned())
}
fn select_all(&self, tree: &mut WidgetTree) {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
let count = state.paragraph.content().graphemes(true).count();
let sel = state
.selection
.get_or_insert_with(|| Box::new(SelectionState::default()));
sel.anchor = 0;
sel.end = count;
}
fn is_focused(&self, tree: &WidgetTree) -> bool {
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
state.is_focused()
}
fn context_menu_position(&self, tree: &WidgetTree) -> Option<Point> {
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
state.context_menu_position()
}
fn set_context_menu_position(
&self,
tree: &mut WidgetTree,
pos: Option<Point>,
) {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
state.set_context_menu_position(pos);
}
}
const DEFAULT_SELECTION_COLOR: Color = Color {
r: 0.0,
g: 0.47,
b: 0.84,
a: 0.3,
};
fn grapheme_to_byte(content: &str, grapheme_index: usize) -> usize {
content
.graphemes(true)
.take(grapheme_index)
.map(|g| g.len())
.sum()
}
fn hit_to_grapheme<P: Paragraph>(
paragraph: &P,
point: Point,
content: &str,
) -> usize {
match paragraph.hit_test(point) {
Some(hit) => {
let byte_offset = hit.cursor().min(content.len());
content[..byte_offset].graphemes(true).count()
}
None => content.graphemes(true).count(),
}
}
fn previous_start_of_word(content: &str, grapheme_index: usize) -> usize {
let graphemes: Vec<&str> = content.graphemes(true).collect();
let clamped = grapheme_index.min(graphemes.len());
let before: String = graphemes[..clamped].concat();
UnicodeSegmentation::split_word_bound_indices(&*before)
.filter(|(_, word)| !word.trim_start().is_empty())
.next_back()
.map_or(0, |(i, prev_word)| {
clamped
- prev_word.graphemes(true).count()
- before[i + prev_word.len()..].graphemes(true).count()
})
}
fn next_end_of_word(content: &str, grapheme_index: usize) -> usize {
let graphemes: Vec<&str> = content.graphemes(true).collect();
let clamped = grapheme_index.min(graphemes.len());
let after: String = graphemes[clamped..].concat();
UnicodeSegmentation::split_word_bound_indices(&*after)
.find(|(_, word)| !word.trim_start().is_empty())
.map_or(graphemes.len(), |(i, next_word)| {
clamped
+ next_word.graphemes(true).count()
+ after[..i].graphemes(true).count()
})
}
fn is_jump_modifier(modifiers: keyboard::Modifiers) -> bool {
if cfg!(target_os = "macos") {
modifiers.alt()
} else {
modifiers.control()
} }
} }

View file

@ -1,5 +1,5 @@
use iced::widget::{button, column, text, Column};
use iced::Center; use iced::Center;
use iced::widget::{Column, button, column, text};
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
iced::run(Counter::update, Counter::view) iced::run(Counter::update, Counter::view)
@ -42,7 +42,7 @@ impl Counter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use iced_test::{simulator, Error}; use iced_test::{Error, simulator};
#[test] #[test]
fn it_counts() -> Result<(), Error> { fn it_counts() -> Result<(), Error> {

View file

@ -140,7 +140,7 @@ impl Pipeline {
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear, mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear,
..Default::default() ..Default::default()
}); });
@ -223,7 +223,7 @@ impl Pipeline {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("cubes pipeline layout"), label: Some("cubes pipeline layout"),
bind_group_layouts: &[&uniform_bind_group_layout], bind_group_layouts: &[&uniform_bind_group_layout],
immediate_size: 0, push_constant_ranges: &[],
}); });
let shader = let shader =
@ -280,7 +280,7 @@ impl Pipeline {
compilation_options: compilation_options:
wgpu::PipelineCompilationOptions::default(), wgpu::PipelineCompilationOptions::default(),
}), }),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -388,7 +388,6 @@ impl Pipeline {
), ),
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}); });
pass.set_viewport( pass.set_viewport(
@ -479,7 +478,7 @@ impl DepthPipeline {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("cubes.depth_pipeline.layout"), label: Some("cubes.depth_pipeline.layout"),
bind_group_layouts: &[&bind_group_layout], bind_group_layouts: &[&bind_group_layout],
immediate_size: 0, push_constant_ranges: &[],
}); });
let shader = let shader =
@ -521,7 +520,7 @@ impl DepthPipeline {
compilation_options: compilation_options:
wgpu::PipelineCompilationOptions::default(), wgpu::PipelineCompilationOptions::default(),
}), }),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -587,7 +586,6 @@ impl DepthPipeline {
), ),
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}); });
pass.set_viewport( pass.set_viewport(

View file

@ -43,7 +43,6 @@ impl Scene {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}) })
} }
@ -65,8 +64,8 @@ fn build_pipeline(
let pipeline_layout = let pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None, label: None,
push_constant_ranges: &[],
bind_group_layouts: &[], bind_group_layouts: &[],
immediate_size: 0,
}); });
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
@ -102,7 +101,7 @@ fn build_pipeline(
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}) })
} }

View file

@ -3,20 +3,20 @@ use std::{
sync::atomic::{AtomicU64, Ordering}, sync::atomic::{AtomicU64, Ordering},
}; };
use iced::{Element, id::Id};
use iced::{ use iced::{
Event, Length, Rectangle,
clipboard::{ clipboard::{
dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent}, dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent},
mime::AllowedMimeTypes, mime::AllowedMimeTypes,
}, },
event, event,
id::Internal, id::Internal,
mouse, overlay, Event, Length, Rectangle, mouse, overlay,
}; };
use iced::{id::Id, Element};
use iced_core::{ use iced_core::{
self, layout, self, Clipboard, Layout, Shell, Widget, layout,
widget::{tree, Tree}, widget::{Tree, tree},
Clipboard, Layout, Shell, Widget,
}; };
pub fn dnd_destination<'a, Message: 'static>( pub fn dnd_destination<'a, Message: 'static>(

View file

@ -1,16 +1,16 @@
use std::any::Any; use std::any::Any;
use iced::Element;
use iced::id::Id; use iced::id::Id;
use iced::widget::container; use iced::widget::container;
use iced::Element;
use iced::{ use iced::{
Event, Length, Point, Rectangle,
clipboard::dnd::{DndAction, DndEvent, SourceEvent}, clipboard::dnd::{DndAction, DndEvent, SourceEvent},
event, mouse, overlay, Event, Length, Point, Rectangle, event, mouse, overlay,
}; };
use iced_core::{ use iced_core::{
layout, renderer, Clipboard, Shell, layout, renderer,
widget::{tree, Tree}, widget::{Tree, tree},
Clipboard, Shell,
}; };
use iced_core::{Layout, Widget}; use iced_core::{Layout, Widget};
@ -41,7 +41,7 @@ impl<
Message: 'static, Message: 'static,
AppMessage: 'static, AppMessage: 'static,
D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static, D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static,
> DndSource<'a, Message, AppMessage, D> > DndSource<'a, Message, AppMessage, D>
{ {
pub fn new(child: impl Into<Element<'a, Message>>) -> Self { pub fn new(child: impl Into<Element<'a, Message>>) -> Self {
Self { Self {
@ -123,7 +123,7 @@ impl<
Message: 'static, Message: 'static,
AppMessage: 'static, AppMessage: 'static,
D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static, D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static,
> Widget<Message, iced::Theme, iced::Renderer> > Widget<Message, iced::Theme, iced::Renderer>
for DndSource<'a, Message, AppMessage, D> for DndSource<'a, Message, AppMessage, D>
{ {
fn children(&self) -> Vec<Tree> { fn children(&self) -> Vec<Tree> {
@ -361,7 +361,7 @@ impl<
Message: 'static, Message: 'static,
AppMessage: 'static, AppMessage: 'static,
D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static, D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static,
> From<DndSource<'a, Message, AppMessage, D>> for Element<'a, Message> > From<DndSource<'a, Message, AppMessage, D>> for Element<'a, Message>
{ {
fn from(e: DndSource<'a, Message, AppMessage, D>) -> Element<'a, Message> { fn from(e: DndSource<'a, Message, AppMessage, D>) -> Element<'a, Message> {
Element::new(e) Element::new(e)

View file

@ -7,6 +7,7 @@ use cctk::sctk::reexports::{
use iced::platform_specific::shell::commands::subsurface::get_subsurface; use iced::platform_specific::shell::commands::subsurface::get_subsurface;
use iced::{ use iced::{
Element, Length, Subscription, Task,
event::wayland::Event as WaylandEvent, event::wayland::Event as WaylandEvent,
platform_specific::{ platform_specific::{
runtime::wayland::subsurface::SctkSubsurfaceSettings, runtime::wayland::subsurface::SctkSubsurfaceSettings,
@ -14,7 +15,6 @@ use iced::{
}, },
widget::{button, column, text, text_input}, widget::{button, column, text, text_input},
window::{self, Id, Settings}, window::{self, Id, Settings},
Element, Length, Subscription, Task,
}; };
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};

View file

@ -477,7 +477,6 @@ fn delete_icon() -> Text<'static> {
fn subtle(theme: &Theme) -> text::Style { fn subtle(theme: &Theme) -> text::Style {
text::Style { text::Style {
color: Some(theme.extended_palette().background.strongest.color), color: Some(theme.extended_palette().background.strongest.color),
..text::Style::default()
} }
} }

View file

@ -1,9 +1,9 @@
//! Load and operate on images. //! Load and operate on images.
#[cfg(feature = "image")] #[cfg(feature = "image")]
use crate::core::Bytes; use crate::core::Bytes;
#[cfg(feature = "image")]
use crate::core::Size;
use crate::core::Color;
use crate::core::Radians;
use crate::core::Rectangle; use crate::core::Rectangle;
use crate::core::image; use crate::core::image;
use crate::core::svg; use crate::core::svg;
@ -180,105 +180,3 @@ fn to_error(error: ::image::ImageError) -> image::Error {
error => image::Error::Invalid(Arc::new(error)), error => image::Error::Invalid(Arc::new(error)),
} }
} }
/// resample a raster image to this target
#[cfg(feature = "image")]
pub fn downsample_target(
native: Size<u32>,
bounds: Size<f32>,
) -> Option<Size<u32>> {
if !(bounds.width >= 1.0 && bounds.height >= 1.0) {
return None;
}
// Round up to 4px so an animated resize does not make a copy per pixel
// TODO: maybe find a better approach
let quantize = |length: f32| (length.ceil() as u32).div_ceil(4) * 4;
let target = Size::new(
quantize(bounds.width).min(native.width),
quantize(bounds.height).min(native.height),
);
let minified = native.width as f32 >= target.width as f32 * 1.25
|| native.height as f32 >= target.height as f32 * 1.25;
// If the target is big then Lanczos will be too expensive, and sampler is good enough
let small = u64::from(target.width) * u64::from(target.height) <= 1 << 18;
(minified && small).then_some(target)
}
/// Resamples premultiplied pixels down to `target`.
#[cfg(feature = "image")]
pub fn downsample_premultiplied(
pixels: &[u8],
size: Size<u32>,
target: Size<u32>,
) -> Vec<u8> {
let image =
::image::RgbaImage::from_raw(size.width, size.height, pixels.to_vec())
.expect("pixels hold width * height RGBA pixels");
resize(&image, target).into_raw()
}
#[cfg(feature = "image")]
fn resize(image: &::image::RgbaImage, target: Size<u32>) -> ::image::RgbaImage {
use ::image::imageops::{self, FilterType};
use std::borrow::Cow;
// if image is too big compared to target, box average it to double the target
// and then do Lanczos. The final result is almost the same, but lanczos is too
// expensive on large images.
let image = if image.width() >= target.width * 4
&& image.height() >= target.height * 4
{
Cow::Owned(imageops::thumbnail(
image,
target.width * 2,
target.height * 2,
))
} else {
Cow::Borrowed(image)
};
imageops::resize(&*image, target.width, target.height, FilterType::Lanczos3)
}
/// Resamples RGBA pixels down to `target`.
///
/// Premultiplies the image, to avoid fringing the edges of an icon when interpolating
/// transparent pixels.
#[cfg(feature = "image")]
pub fn downsample(image: &Buffer, target: Size<u32>) -> ::image::RgbaImage {
let mut image = ::image::RgbaImage::from_raw(
image.width(),
image.height(),
image.as_raw().to_vec(),
)
.expect("buffer holds width * height RGBA pixels");
for pixel in image.pixels_mut() {
let alpha = u32::from(pixel[3]);
for channel in &mut pixel.0[..3] {
*channel = ((u32::from(*channel) * alpha + 127) / 255) as u8;
}
}
let mut image = resize(&image, target);
for pixel in image.pixels_mut() {
let alpha = u32::from(pixel[3]);
if alpha > 0 {
for channel in &mut pixel.0[..3] {
*channel = ((u32::from(*channel) * 255 + alpha / 2) / alpha)
.min(255) as u8;
}
}
}
image
}

View file

@ -83,20 +83,13 @@ impl<T: Layer> Stack<T> {
/// Pushes a new clipping region in the [`Stack`]; creating a new layer in the /// Pushes a new clipping region in the [`Stack`]; creating a new layer in the
/// process. /// process.
pub fn push_clip(&mut self, bounds: Rectangle) { pub fn push_clip(&mut self, bounds: Rectangle) {
let bounds = bounds * self.transformation();
let bounds = bounds
.intersection(&self.layers[self.current].bounds())
.unwrap_or(Rectangle {
width: 0.0,
height: 0.0,
..bounds
});
self.previous.push(self.current); self.previous.push(self.current);
self.current = self.active_count; self.current = self.active_count;
self.active_count += 1; self.active_count += 1;
let bounds = bounds * self.transformation();
if self.current == self.layers.len() { if self.current == self.layers.len() {
self.layers.push(T::with_bounds(bounds)); self.layers.push(T::with_bounds(bounds));
} else { } else {

View file

@ -161,14 +161,6 @@ impl FontSystem {
&mut self.raw &mut self.raw
} }
/// Returns a reference to the underlying [`fontdb::Database`].
///
/// Unlike going through [`raw`](Self::raw) and `db_mut`, this does not
/// clear `cosmic-text`'s internal font-match cache.
pub fn db(&self) -> &cosmic_text::fontdb::Database {
self.raw.db()
}
/// Loads a font from its bytes. /// Loads a font from its bytes.
pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) { pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
if let Cow::Borrowed(bytes) = bytes { if let Cow::Borrowed(bytes) = bytes {

View file

@ -336,10 +336,6 @@ impl editor::Editor for Editor {
} }
} }
Action::ClearSelection => {
editor.set_selection(cosmic_text::Selection::None);
}
// Editing events // Editing events
Action::Edit(edit) => { Action::Edit(edit) => {
let topmost_line_before_edit = editor let topmost_line_before_edit = editor

View file

@ -17,7 +17,7 @@ workspace = true
debug = [] debug = []
selector = ["dep:iced_selector"] selector = ["dep:iced_selector"]
a11y = ["iced_accessibility", "iced_core/a11y"] a11y = ["iced_accessibility", "iced_core/a11y"]
wayland = ["iced_core/wayland", "dep:cctk"] cctk = ["iced_core/cctk", "dep:cctk"]
[dependencies] [dependencies]
bytes.workspace = true bytes.workspace = true
@ -33,14 +33,9 @@ sipper.optional = true
iced_selector.workspace = true iced_selector.workspace = true
iced_selector.optional = true iced_selector.optional = true
iced_futures.features = ["thread-pool"] iced_futures.features = ["thread-pool"]
cctk.workspace = true
cctk.optional = true
iced_accessibility.workspace = true iced_accessibility.workspace = true
iced_accessibility.optional = true iced_accessibility.optional = true
window_clipboard.workspace = true window_clipboard.workspace = true
dnd.workspace = true dnd.workspace = true
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
cctk.workspace = true
cctk.optional = true
[build-dependencies]
build_helpers = { path = "../build_helpers" }

View file

@ -1,3 +0,0 @@
fn main() {
build_helpers::cfg_aliases_setup();
}

View file

@ -1,10 +1,12 @@
//! Access the clipboard. //! Access the clipboard.
use std::any::Any;
use dnd::{DndDestinationRectangle, DndSurface}; use dnd::{DndDestinationRectangle, DndSurface};
use window_clipboard::mime::AllowedMimeTypes; use iced_core::clipboard::DndSource;
use window_clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
use crate::{Action, Task, oneshot, task}; use crate::{oneshot, task, Action, Task};
/// An action to be performed on the system. /// An action to be performed on the system.
pub enum DndAction { pub enum DndAction {

View file

@ -28,6 +28,7 @@ pub use task::Task;
pub use user_interface::UserInterface; pub use user_interface::UserInterface;
pub use window::Window; pub use window::Window;
use crate::core::Color;
use crate::futures::futures::channel::oneshot; use crate::futures::futures::channel::oneshot;
use std::borrow::Cow; use std::borrow::Cow;

View file

@ -2,23 +2,23 @@
use std::fmt; use std::fmt;
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
/// Platform specific actions defined for wayland /// Platform specific actions defined for wayland
pub mod wayland; pub mod wayland;
/// Platform specific actions defined for wayland /// Platform specific actions defined for wayland
pub enum Action { pub enum Action {
/// Wayland Specific Actions /// Wayland Specific Actions
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
Wayland(wayland::Action), Wayland(wayland::Action),
} }
impl fmt::Debug for Action { impl fmt::Debug for Action {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
Action::Wayland(action) => action.fmt(_f), Action::Wayland(action) => action.fmt(_f),
#[cfg(not(wayland_platform))] #[cfg(not(all(feature = "cctk", target_os = "linux")))]
_ => Ok(()), _ => Ok(()),
} }
} }

View file

@ -36,7 +36,7 @@ impl fmt::Debug for Action {
f, f,
"Action::ActivationAction::Activate {{ window: {:?}, token: {:?} }}", "Action::ActivationAction::Activate {{ window: {:?}, token: {:?} }}",
window, token, window, token,
), )
} }
} }
} }

View file

@ -6,8 +6,8 @@ use cctk::sctk::{
}; };
use iced_core::layout::Limits; use iced_core::layout::Limits;
use iced_core::Rectangle;
use iced_core::window::Id; use iced_core::window::Id;
use iced_core::Rectangle;
/// output for layer surface /// output for layer surface
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -150,13 +150,6 @@ pub enum Action {
/// layer of the layer surface /// layer of the layer surface
layer: Layer, layer: Layer,
}, },
/// padding of the surface geometry
Padding {
/// id of the layer surface
id: Id,
/// padding of the surface geometry
padding: IcedMargin,
},
} }
impl fmt::Debug for Action { impl fmt::Debug for Action {
@ -169,51 +162,35 @@ impl fmt::Debug for Action {
), ),
Action::Size { id, width, height } => write!( Action::Size { id, width, height } => write!(
f, f,
"Action::LayerSurfaceAction::Size {{ id: {:#?}, width: {:?}, height: {:?} }}", "Action::LayerSurfaceAction::Size {{ id: {:#?}, width: {:?}, height: {:?} }}", id, width, height
id, width, height
), ),
Action::Destroy(id) => write!( Action::Destroy(id) => write!(
f, f,
"Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}", "Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}", id
id
), ),
Action::Anchor { id, anchor } => write!( Action::Anchor { id, anchor } => write!(
f, f,
"Action::LayerSurfaceAction::Anchor {{ id: {:#?}, anchor: {:?} }}", "Action::LayerSurfaceAction::Anchor {{ id: {:#?}, anchor: {:?} }}", id, anchor
id, anchor
), ),
Action::ExclusiveZone { id, exclusive_zone } => write!( Action::ExclusiveZone { id, exclusive_zone } => write!(
f, f,
"Action::LayerSurfaceAction::ExclusiveZone {{ id: {:#?}, exclusive_zone: {exclusive_zone} }}", "Action::LayerSurfaceAction::ExclusiveZone {{ id: {:#?}, exclusive_zone: {exclusive_zone} }}", id
id
), ),
Action::Margin { id, margin } => write!( Action::Margin { id, margin } => write!(
f, f,
"Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}", "Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}", id, margin
id, margin
), ),
Action::KeyboardInteractivity { Action::KeyboardInteractivity { id, keyboard_interactivity } => write!(
id,
keyboard_interactivity,
} => write!(
f, f,
"Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}", "Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}", id, keyboard_interactivity
id, keyboard_interactivity
), ),
Action::InputZone { id, zone } => write!( Action::InputZone { id, zone } => write!(
f, f,
"Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}", "Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}", id, zone
id, zone
), ),
Action::Layer { id, layer } => write!( Action::Layer { id, layer } => write!(
f, f,
"Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}", "Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}", id, layer
id, layer
),
Action::Padding { id, padding } => write!(
f,
"Action::LayerSurfaceAction::Padding {{ id: {:#?}, padding: {:?} }}",
id, padding
), ),
} }
} }

View file

@ -124,34 +124,26 @@ pub enum Action {
/// height /// height
height: u32, height: u32,
}, },
/// set the size of the popup
Reposition {
/// id of the popup
id: Id,
/// positioner of the popup
positioner: SctkPositioner,
},
} }
impl fmt::Debug for Action { impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Action::Popup { popup, .. } => { Action::Popup { popup, .. } => write!(
write!(f, "Action::PopupAction::Popup {{ popup: {:?} }}", popup) f,
} "Action::PopupAction::Popup {{ popup: {:?} }}",
Action::Destroy { id } => { popup
write!(f, "Action::PopupAction::Destroy {{ id: {:?} }}", id) ),
} Action::Destroy { id } => write!(
f,
"Action::PopupAction::Destroy {{ id: {:?} }}",
id
),
Action::Size { id, width, height } => write!( Action::Size { id, width, height } => write!(
f, f,
"Action::PopupAction::Size {{ id: {:?}, width: {:?}, height: {:?} }}", "Action::PopupAction::Size {{ id: {:?}, width: {:?}, height: {:?} }}",
id, width, height id, width, height
), ),
Action::Reposition { id, positioner } => write!(
f,
"Action::Reposition {{ id: {:?}, positioner: {:?} }}",
id, positioner
),
} }
} }
} }

View file

@ -222,7 +222,7 @@ pub fn frames() -> Subscription<(Id, Instant)> {
_ => None, _ => None,
}) })
} }
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
/// Subscribes to the frames of the window of the running application. /// Subscribes to the frames of the window of the running application.
/// ///
/// The resulting [`Subscription`] will produce items at a rate equal to the /// The resulting [`Subscription`] will produce items at a rate equal to the

View file

@ -606,7 +606,7 @@ pub mod font {
pub mod event { pub mod event {
//! Handle events of a user interface. //! Handle events of a user interface.
pub use crate::core::event::PlatformSpecific; pub use crate::core::event::PlatformSpecific;
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
pub use crate::core::event::wayland; pub use crate::core::event::wayland;
pub use crate::core::event::{Event, Status}; pub use crate::core::event::{Event, Status};
pub use iced_futures::event::{listen, listen_raw, listen_with}; pub use iced_futures::event::{listen, listen_raw, listen_with};
@ -683,8 +683,6 @@ pub use event::Event;
pub use executor::Executor; pub use executor::Executor;
pub use font::Font; pub use font::Font;
pub use renderer::Renderer; pub use renderer::Renderer;
#[cfg(feature = "canvas")]
pub use renderer::graphics::geometry;
pub use task::Task; pub use task::Task;
pub use window::Window; pub use window::Window;

View file

@ -775,7 +775,6 @@ impl<P: Program + 'static> Tester<P> {
} }
_ => None, _ => None,
}, },
..text::Style::default()
}) })
.into() .into()
}, },
@ -946,7 +945,6 @@ where
text(label).size(14).style(|theme: &core::Theme| { text(label).size(14).style(|theme: &core::Theme| {
text::Style { text::Style {
color: Some(theme.extended_palette().background.weak.text), color: Some(theme.extended_palette().background.weak.text),
..text::Style::default()
} }
}), }),
space::horizontal(), space::horizontal(),

View file

@ -263,10 +263,6 @@ impl Engine {
} else { } else {
// Draw corners that have too small border radii as having no border radius, // Draw corners that have too small border radii as having no border radius,
// but mask them with the rounded rectangle with the correct border radius. // but mask them with the rounded rectangle with the correct border radius.
if path_bounds.width < 1.0 || path_bounds.height < 1.0 {
return;
}
let mut temp_pixmap = tiny_skia::Pixmap::new( let mut temp_pixmap = tiny_skia::Pixmap::new(
path_bounds.width as u32, path_bounds.width as u32,
path_bounds.height as u32, path_bounds.height as u32,

View file

@ -51,21 +51,7 @@ impl Pipeline {
) { ) {
let mut cache = self.cache.borrow_mut(); let mut cache = self.cache.borrow_mut();
let target = { let Ok(mut image) = cache.allocate(handle) else {
let Ok(image) = cache.allocate(handle) else {
return;
};
graphics::image::downsample_target(
Size::new(image.width(), image.height()),
bounds.size(),
)
};
let Ok(mut image) = (match target {
Some(target) => cache.allocate_resampled(handle, target),
None => cache.allocate(handle),
}) else {
return; return;
}; };
@ -124,8 +110,6 @@ impl Pipeline {
struct Cache { struct Cache {
entries: FxHashMap<raster::Id, Option<Entry>>, entries: FxHashMap<raster::Id, Option<Entry>>,
hits: FxHashSet<raster::Id>, hits: FxHashSet<raster::Id>,
resampled: FxHashMap<(raster::Id, u32, u32), Entry>,
resampled_hits: FxHashSet<(raster::Id, u32, u32)>,
} }
impl Cache { impl Cache {
@ -182,59 +166,9 @@ impl Cache {
Ok(ret) Ok(ret)
} }
/// Like [`Self::allocate`], resampled to `target`. Call after
/// [`Self::allocate`] has decoded the image.
pub fn allocate_resampled(
&mut self,
handle: &raster::Handle,
target: Size<u32>,
) -> Result<tiny_skia::PixmapRef<'_>, raster::Error> {
let key = (handle.id(), target.width, target.height);
if !self.resampled.contains_key(&key) {
let native = self
.entries
.get(&handle.id())
.and_then(Option::as_ref)
.ok_or(raster::Error::Empty)?;
// Stored pixels are already premultiplied, so resample them as is.
let pixels = graphics::image::downsample_premultiplied(
bytemuck::cast_slice(&native.pixels),
Size::new(native.width, native.height),
target,
);
let _ = self.resampled.insert(
key,
Entry {
width: target.width,
height: target.height,
pixels: pixels
.chunks_exact(4)
.map(|p| u32::from_ne_bytes([p[0], p[1], p[2], p[3]]))
.collect(),
},
);
}
let _ = self.resampled_hits.insert(key);
let entry = &self.resampled[&key];
Ok(tiny_skia::PixmapRef::from_bytes(
bytemuck::cast_slice(&entry.pixels),
entry.width,
entry.height,
)
.expect("Build pixmap from image bytes"))
}
fn trim(&mut self) { fn trim(&mut self) {
self.entries.retain(|key, _| self.hits.contains(key)); self.entries.retain(|key, _| self.hits.contains(key));
self.resampled
.retain(|key, _| self.resampled_hits.contains(key));
self.hits.clear(); self.hits.clear();
self.resampled_hits.clear();
} }
} }

View file

@ -1,6 +1,5 @@
use crate::core::svg::{Data, Handle}; use crate::core::svg::{Data, Handle};
use crate::core::{Color, Rectangle, Size}; use crate::core::{Color, Rectangle, Size};
use crate::graphics::text;
use resvg::usvg; use resvg::usvg;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
@ -75,7 +74,6 @@ struct Cache {
rasters: FxHashMap<RasterKey, tiny_skia::Pixmap>, rasters: FxHashMap<RasterKey, tiny_skia::Pixmap>,
raster_hits: FxHashSet<RasterKey>, raster_hits: FxHashSet<RasterKey>,
fontdb: Option<Arc<usvg::fontdb::Database>>, fontdb: Option<Arc<usvg::fontdb::Database>>,
fontdb_version: Option<text::Version>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -89,15 +87,12 @@ impl Cache {
fn load(&mut self, handle: &Handle) -> Option<&usvg::Tree> { fn load(&mut self, handle: &Handle) -> Option<&usvg::Tree> {
let id = handle.id(); let id = handle.id();
// Reuse the font database already loaded by the `cosmic-text` font // TODO: Reuse `cosmic-text` font database
// system. We clone it once and refresh only when a font is if self.fontdb.is_none() {
// loaded (i.e. when the font system's version changes). let mut fontdb = usvg::fontdb::Database::new();
let font_system = text::font_system().read().expect("Read font system"); fontdb.load_system_fonts();
let version = font_system.version();
if self.fontdb_version != Some(version) { self.fontdb = Some(Arc::new(fontdb));
self.fontdb = Some(Arc::new(font_system.db().clone()));
self.fontdb_version = Some(version);
} }
let options = usvg::Options { let options = usvg::Options {

View file

@ -25,12 +25,6 @@ svg = ["iced_graphics/svg", "resvg/text"]
web-colors = ["iced_graphics/web-colors"] web-colors = ["iced_graphics/web-colors"]
webgl = ["wgpu/webgl"] webgl = ["wgpu/webgl"]
strict-assertions = [] strict-assertions = []
# Yoda: opt-in X11 backend. Pulls tiny-xlib/as-raw-xcb-connection/x11rb
# (which otherwise were unconditional direct deps on unix). Wayland-only
# builds just leave this off — window/x11.rs and the DRI3 device-id
# fallback in window/compositor.rs are cfg-gated on it.
x11 = ["dep:as-raw-xcb-connection", "dep:tiny-xlib", "dep:x11rb"]
wayland = ["cctk"]
[dependencies] [dependencies]
iced_debug.workspace = true iced_debug.workspace = true
@ -63,14 +57,11 @@ wayland-protocols.workspace = true
wayland-backend = { version = "0.3.3", features = ["client_system"] } wayland-backend = { version = "0.3.3", features = ["client_system"] }
wayland-client = { version = "0.31.2" } wayland-client = { version = "0.31.2" }
wayland-sys = { version = "0.31.1", features = ["dlopen"] } wayland-sys = { version = "0.31.1", features = ["dlopen"] }
as-raw-xcb-connection = { version = "1.0.1", optional = true } as-raw-xcb-connection = "1.0.1"
tiny-xlib = { version = "0.2.3", optional = true } tiny-xlib = "0.2.3"
x11rb = { version = "0.13.1", optional = true, features = [ x11rb = { version = "0.13.1", features = [
"allow-unsafe-code", "allow-unsafe-code",
"dl-libxcb", "dl-libxcb",
"dri3", "dri3",
"randr", "randr",
] } ] }
[build-dependencies]
build_helpers = { path = "../build_helpers" }

View file

@ -1,3 +0,0 @@
fn main() {
build_helpers::cfg_aliases_setup();
}

View file

@ -63,6 +63,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
/// Returns the size of the written bytes. /// Returns the size of the written bytes.
pub fn write( pub fn write(
&mut self, &mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder, encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt, belt: &mut wgpu::util::StagingBelt,
offset: usize, offset: usize,
@ -78,6 +79,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
&self.raw, &self.raw,
(offset + bytes_written) as u64, (offset + bytes_written) as u64,
MAX_WRITE_SIZE_U64, MAX_WRITE_SIZE_U64,
device,
) )
.copy_from_slice( .copy_from_slice(
&bytes[bytes_written..bytes_written + MAX_WRITE_SIZE], &bytes[bytes_written..bytes_written + MAX_WRITE_SIZE],
@ -98,6 +100,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
&self.raw, &self.raw,
(offset + bytes_written) as u64, (offset + bytes_written) as u64,
bytes_left, bytes_left,
device,
) )
.copy_from_slice(&bytes[bytes_written..]); .copy_from_slice(&bytes[bytes_written..]);

View file

@ -99,7 +99,7 @@ pub fn convert(
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.offscreen.blit.pipeline_layout"), label: Some("iced_wgpu.offscreen.blit.pipeline_layout"),
bind_group_layouts: &[&constant_layout, &texture_layout], bind_group_layouts: &[&constant_layout, &texture_layout],
immediate_size: 0, push_constant_ranges: &[],
}); });
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
@ -149,7 +149,7 @@ pub fn convert(
}, },
depth_stencil: None, depth_stencil: None,
multisample: wgpu::MultisampleState::default(), multisample: wgpu::MultisampleState::default(),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -194,7 +194,6 @@ pub fn convert(
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}); });
pass.set_pipeline(&pipeline); pass.set_pipeline(&pipeline);

View file

@ -132,7 +132,7 @@ impl Atlas {
match &entry { match &entry {
Entry::Contiguous(allocation) => { Entry::Contiguous(allocation) => {
self.upload_allocation( self.upload_allocation(
pixels, width, 0, allocation, encoder, belt, pixels, width, 0, allocation, device, encoder, belt,
); );
} }
Entry::Fragmented { fragments, .. } => { Entry::Fragmented { fragments, .. } => {
@ -145,6 +145,7 @@ impl Atlas {
width, width,
offset, offset,
&fragment.allocation, &fragment.allocation,
device,
encoder, encoder,
belt, belt,
); );
@ -311,6 +312,7 @@ impl Atlas {
image_width: u32, image_width: u32,
offset: usize, offset: usize,
allocation: &Allocation, allocation: &Allocation,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder, encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt, belt: &mut wgpu::util::StagingBelt,
) { ) {
@ -332,6 +334,7 @@ impl Atlas {
let buffer_slice = belt.allocate( let buffer_slice = belt.allocate(
wgpu::BufferSize::new(total_bytes as u64).unwrap(), wgpu::BufferSize::new(total_bytes as u64).unwrap(),
wgpu::BufferSize::new(8 * 4).unwrap(), wgpu::BufferSize::new(8 * 4).unwrap(),
device,
); );
const PIXEL: usize = 4; const PIXEL: usize = 4;

View file

@ -5,8 +5,6 @@ use crate::image::atlas::{self, Atlas};
#[cfg(all(feature = "image", not(target_arch = "wasm32")))] #[cfg(all(feature = "image", not(target_arch = "wasm32")))]
use worker::Worker; use worker::Worker;
#[cfg(feature = "image")]
use rustc_hash::{FxHashMap, FxHashSet};
#[cfg(feature = "image")] #[cfg(feature = "image")]
use std::collections::HashMap; use std::collections::HashMap;
@ -40,13 +38,7 @@ impl Cache {
raster: Raster { raster: Raster {
cache: crate::image::raster::Cache::default(), cache: crate::image::raster::Cache::default(),
pending: HashMap::new(), pending: HashMap::new(),
resampled: FxHashMap::default(), belt: wgpu::util::StagingBelt::new(2 * 1024 * 1024),
resampled_hits: FxHashSet::default(),
should_trim: false,
belt: wgpu::util::StagingBelt::new(
device.clone(),
2 * 1024 * 1024,
),
}, },
#[cfg(feature = "svg")] #[cfg(feature = "svg")]
vector: crate::image::vector::Cache::default(), vector: crate::image::vector::Cache::default(),
@ -214,9 +206,7 @@ impl Cache {
encoder: &mut wgpu::CommandEncoder, encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt, belt: &mut wgpu::util::StagingBelt,
handle: &core::image::Handle, handle: &core::image::Handle,
bounds: Size<f32>,
) -> Option<(&atlas::Entry, &Arc<wgpu::BindGroup>)> { ) -> Option<(&atlas::Entry, &Arc<wgpu::BindGroup>)> {
use crate::graphics::image::{downsample, downsample_target, load};
use crate::image::raster::Memory; use crate::image::raster::Memory;
self.receive(); self.receive();
@ -230,34 +220,6 @@ impl Cache {
None, None,
)?; )?;
if let Some(target) = downsample_target(memory.dimensions(), bounds) {
let key = (handle.id(), target.width, target.height);
if !self.raster.resampled.contains_key(&key) {
let image = memory.host().or_else(|| load(handle).ok())?;
let image = downsample(&image, target);
let entry = self.atlas.upload(
device,
encoder,
belt,
target.width,
target.height,
&image,
)?;
let _ = self.raster.resampled.insert(key, entry);
self.raster.should_trim = true;
}
let _ = self.raster.resampled_hits.insert(key);
return Some((
self.raster.resampled.get(&key)?,
self.atlas.bind_group(),
));
}
if let Memory::Device { if let Memory::Device {
entry, bind_group, .. entry, bind_group, ..
} = memory } = memory
@ -338,23 +300,6 @@ impl Cache {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
self.worker.drop(_bind_group); self.worker.drop(_bind_group);
}); });
if self.raster.should_trim {
let hits = &self.raster.resampled_hits;
let atlas = &mut self.atlas;
self.raster.resampled.retain(|key, entry| {
let retain = hits.contains(key);
if !retain {
atlas.remove(entry);
}
retain
});
self.raster.resampled_hits.clear();
self.raster.should_trim = false;
}
} }
#[cfg(feature = "svg")] #[cfg(feature = "svg")]
@ -428,9 +373,6 @@ impl Drop for Cache {
struct Raster { struct Raster {
cache: crate::image::raster::Cache, cache: crate::image::raster::Cache,
pending: HashMap<core::image::Id, Vec<Callback>>, pending: HashMap<core::image::Id, Vec<Callback>>,
resampled: FxHashMap<(core::image::Id, u32, u32), atlas::Entry>,
resampled_hits: FxHashSet<(core::image::Id, u32, u32)>,
should_trim: bool,
belt: wgpu::util::StagingBelt, belt: wgpu::util::StagingBelt,
} }
@ -509,10 +451,7 @@ mod worker {
backend, backend,
texture_layout, texture_layout,
shell: shell.clone(), shell: shell.clone(),
belt: wgpu::util::StagingBelt::new( belt: wgpu::util::StagingBelt::new(4 * 1024 * 1024),
device.clone(),
4 * 1024 * 1024,
),
jobs: jobs_receiver, jobs: jobs_receiver,
output: work_sender, output: work_sender,
quit: quit_receiver, quit: quit_receiver,

View file

@ -45,7 +45,7 @@ impl Pipeline {
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mag_filter: wgpu::FilterMode::Nearest, mag_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::MipmapFilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
}); });
@ -55,7 +55,7 @@ impl Pipeline {
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
min_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear,
mag_filter: wgpu::FilterMode::Linear, mag_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear,
..Default::default() ..Default::default()
}); });
@ -106,8 +106,8 @@ impl Pipeline {
let layout = let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu::image pipeline layout"), label: Some("iced_wgpu::image pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&constant_layout, &texture_layout], bind_group_layouts: &[&constant_layout, &texture_layout],
immediate_size: 0,
}); });
let shader = let shader =
@ -191,7 +191,7 @@ impl Pipeline {
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -267,13 +267,7 @@ impl State {
clip_bounds, clip_bounds,
} => { } => {
if let Some((atlas_entry, bind_group)) = cache if let Some((atlas_entry, bind_group)) = cache
.upload_raster( .upload_raster(device, encoder, belt, &image.handle)
device,
encoder,
belt,
&image.handle,
bounds.size() * scale,
)
{ {
match atlas.as_mut() { match atlas.as_mut() {
None => { None => {
@ -534,6 +528,7 @@ impl Layer {
&self.uniforms, &self.uniforms,
0, 0,
(bytes.len() as u64).try_into().expect("Sized uniforms"), (bytes.len() as u64).try_into().expect("Sized uniforms"),
device,
) )
.copy_from_slice(bytes); .copy_from_slice(bytes);
@ -544,11 +539,11 @@ impl Layer {
let mut offset = 0; let mut offset = 0;
if !nearest.is_empty() { if !nearest.is_empty() {
offset += self.instances.write(encoder, belt, 0, nearest); offset += self.instances.write(device, encoder, belt, 0, nearest);
} }
if !linear.is_empty() { if !linear.is_empty() {
let _ = self.instances.write(encoder, belt, offset, linear); let _ = self.instances.write(device, encoder, belt, offset, linear);
} }
} }

View file

@ -126,7 +126,6 @@ impl Renderer {
// It would be great if the `StagingBelt` API exposed methods // It would be great if the `StagingBelt` API exposed methods
// for introspection to detect when a resize may be worth it. // for introspection to detect when a resize may be worth it.
staging_belt: wgpu::util::StagingBelt::new( staging_belt: wgpu::util::StagingBelt::new(
engine.device.clone(),
buffer::MAX_WRITE_SIZE as u64, buffer::MAX_WRITE_SIZE as u64,
), ),
@ -455,7 +454,6 @@ impl Renderer {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}, },
)); ));
@ -530,7 +528,6 @@ impl Renderer {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}, },
)); ));
} }
@ -624,7 +621,6 @@ impl Renderer {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}, },
)); ));
} }

View file

@ -209,7 +209,7 @@ impl Layer {
transformation: Transformation, transformation: Transformation,
scale: f32, scale: f32,
) { ) {
self.update(encoder, belt, transformation, scale); self.update(device, encoder, belt, transformation, scale);
if !quads.solids.is_empty() { if !quads.solids.is_empty() {
self.solid.prepare(device, encoder, belt, &quads.solids); self.solid.prepare(device, encoder, belt, &quads.solids);
@ -223,6 +223,7 @@ impl Layer {
pub fn update( pub fn update(
&mut self, &mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder, encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt, belt: &mut wgpu::util::StagingBelt,
transformation: Transformation, transformation: Transformation,
@ -236,6 +237,7 @@ impl Layer {
&self.constants_buffer, &self.constants_buffer,
0, 0,
(bytes.len() as u64).try_into().expect("Sized uniforms"), (bytes.len() as u64).try_into().expect("Sized uniforms"),
device,
) )
.copy_from_slice(bytes); .copy_from_slice(bytes);
} }

View file

@ -51,7 +51,7 @@ impl Layer {
instances: &[Gradient], instances: &[Gradient],
) { ) {
let _ = self.instances.resize(device, instances.len()); let _ = self.instances.resize(device, instances.len());
let _ = self.instances.write(encoder, belt, 0, instances); let _ = self.instances.write(device, encoder, belt, 0, instances);
self.instance_count = instances.len(); self.instance_count = instances.len();
} }
@ -75,8 +75,8 @@ impl Pipeline {
let layout = device.create_pipeline_layout( let layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor { &wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.quad.gradient.pipeline"), label: Some("iced_wgpu.quad.gradient.pipeline"),
push_constant_ranges: &[],
bind_group_layouts: &[constants_layout], bind_group_layouts: &[constants_layout],
immediate_size: 0,
}, },
); );
@ -155,7 +155,7 @@ impl Pipeline {
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}, },
); );

View file

@ -45,7 +45,7 @@ impl Layer {
instances: &[Solid], instances: &[Solid],
) { ) {
let _ = self.instances.resize(device, instances.len()); let _ = self.instances.resize(device, instances.len());
let _ = self.instances.write(encoder, belt, 0, instances); let _ = self.instances.write(device, encoder, belt, 0, instances);
self.instance_count = instances.len(); self.instance_count = instances.len();
} }
@ -65,8 +65,8 @@ impl Pipeline {
let layout = let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.quad.solid.pipeline"), label: Some("iced_wgpu.quad.solid.pipeline"),
push_constant_ranges: &[],
bind_group_layouts: &[constants_layout], bind_group_layouts: &[constants_layout],
immediate_size: 0,
}); });
let shader = let shader =
@ -139,7 +139,7 @@ impl Pipeline {
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });

View file

@ -619,7 +619,7 @@ fn prepare(
)?; )?;
Some(cryoglyph::TextArea { Some(cryoglyph::TextArea {
text: buffer.layout_runs(), buffer,
left: position.x, left: position.x,
top: position.y, top: position.y,
scale: transformation.scale_factor() scale: transformation.scale_factor()

View file

@ -313,7 +313,6 @@ fn render<'a>(
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}) })
}; };
@ -426,13 +425,18 @@ impl Layer {
let indices = mesh.indices(); let indices = mesh.indices();
index_offset += index_offset += self.index_buffer.write(
self.index_buffer device,
.write(encoder, belt, index_offset, indices); encoder,
belt,
index_offset,
indices,
);
match mesh { match mesh {
Mesh::Solid { buffers, .. } => { Mesh::Solid { buffers, .. } => {
solid_vertex_offset += self.solid.vertices.write( solid_vertex_offset += self.solid.vertices.write(
device,
encoder, encoder,
belt, belt,
solid_vertex_offset, solid_vertex_offset,
@ -440,14 +444,16 @@ impl Layer {
); );
solid_uniform_offset += self.solid.uniforms.write( solid_uniform_offset += self.solid.uniforms.write(
device,
encoder, encoder,
belt, belt,
solid_uniform_offset, solid_uniform_offset,
&[uniforms], &[uniforms],
) );
} }
Mesh::Gradient { buffers, .. } => { Mesh::Gradient { buffers, .. } => {
gradient_vertex_offset += self.gradient.vertices.write( gradient_vertex_offset += self.gradient.vertices.write(
device,
encoder, encoder,
belt, belt,
gradient_vertex_offset, gradient_vertex_offset,
@ -455,6 +461,7 @@ impl Layer {
); );
gradient_uniform_offset += self.gradient.uniforms.write( gradient_uniform_offset += self.gradient.uniforms.write(
device,
encoder, encoder,
belt, belt,
gradient_uniform_offset, gradient_uniform_offset,
@ -724,7 +731,7 @@ mod solid {
&wgpu::PipelineLayoutDescriptor { &wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.triangle.solid.pipeline_layout"), label: Some("iced_wgpu.triangle.solid.pipeline_layout"),
bind_group_layouts: &[&constants_layout], bind_group_layouts: &[&constants_layout],
immediate_size: 0, push_constant_ranges: &[],
}, },
); );
@ -777,7 +784,7 @@ mod solid {
primitive: triangle::primitive_state(), primitive: triangle::primitive_state(),
depth_stencil: None, depth_stencil: None,
multisample: triangle::multisample_state(antialiasing), multisample: triangle::multisample_state(antialiasing),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}, },
); );
@ -879,7 +886,7 @@ mod gradient {
&wgpu::PipelineLayoutDescriptor { &wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.triangle.gradient.pipeline_layout"), label: Some("iced_wgpu.triangle.gradient.pipeline_layout"),
bind_group_layouts: &[&constants_layout], bind_group_layouts: &[&constants_layout],
immediate_size: 0, push_constant_ranges: &[],
}, },
); );
@ -942,7 +949,7 @@ mod gradient {
primitive: triangle::primitive_state(), primitive: triangle::primitive_state(),
depth_stencil: None, depth_stencil: None,
multisample: triangle::multisample_state(antialiasing), multisample: triangle::multisample_state(antialiasing),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}, },
); );

View file

@ -69,8 +69,8 @@ impl Pipeline {
let layout = let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu::triangle::msaa pipeline layout"), label: Some("iced_wgpu::triangle::msaa pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&constant_layout, &texture_layout], bind_group_layouts: &[&constant_layout, &texture_layout],
immediate_size: 0,
}); });
let shader = let shader =
@ -116,7 +116,7 @@ impl Pipeline {
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -177,7 +177,6 @@ impl Pipeline {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}) })
} }
} }
@ -321,6 +320,7 @@ impl State {
0, 0,
NonZeroU64::new(std::mem::size_of::<Ratio>() as u64) NonZeroU64::new(std::mem::size_of::<Ratio>() as u64)
.expect("non-empty ratio"), .expect("non-empty ratio"),
device,
) )
.copy_from_slice(bytemuck::bytes_of(&ratio)); .copy_from_slice(bytemuck::bytes_of(&ratio));
@ -351,7 +351,6 @@ impl State {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}); });
render_pass.set_pipeline(&pipeline.raw); render_pass.set_pipeline(&pipeline.raw);

View file

@ -1,8 +1,13 @@
//! Display rendering results on windows. //! Display rendering results on windows.
pub mod compositor; pub mod compositor;
#[cfg(wayland_platform)] #[cfg(all(
unix,
feature = "cctk",
not(target_os = "macos"),
not(target_os = "redox")
))]
mod wayland; mod wayland;
#[cfg(all(unix, feature = "x11", not(target_os = "macos"), not(target_os = "redox")))] #[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
mod x11; mod x11;
pub use compositor::Compositor; pub use compositor::Compositor;

View file

@ -9,10 +9,16 @@ use crate::graphics::{self, Shell, Viewport};
use crate::settings::{self, Settings}; use crate::settings::{self, Settings};
use crate::{Engine, Renderer}; use crate::{Engine, Renderer};
#[cfg(wayland_platform)] #[cfg(all(
unix,
feature = "cctk",
not(target_os = "macos"),
not(target_os = "redox")
))]
use super::wayland::get_wayland_device_ids; use super::wayland::get_wayland_device_ids;
#[cfg(all(unix, feature = "x11", not(target_os = "macos"), not(target_os = "redox")))] #[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
use super::x11::get_x11_device_ids; use super::x11::get_x11_device_ids;
use std::future::Future;
/// A window graphics backend for iced powered by `wgpu`. /// A window graphics backend for iced powered by `wgpu`.
pub struct Compositor { pub struct Compositor {
@ -59,12 +65,15 @@ impl Compositor {
compatible_window: Option<W>, compatible_window: Option<W>,
shell: Shell, shell: Shell,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
#[cfg(wayland_platform)] #[cfg(all(
unix,
feature = "cctk",
not(target_os = "macos"),
not(target_os = "redox")
))]
let ids = compatible_window.as_ref().and_then(|window| { let ids = compatible_window.as_ref().and_then(|window| {
let w = get_wayland_device_ids(window); get_wayland_device_ids(window)
#[cfg(feature = "x11")] .or_else(|| get_x11_device_ids(window))
let w = w.or_else(|| get_x11_device_ids(window));
w
}); });
// HACK: // HACK:
@ -73,7 +82,12 @@ impl Compositor {
// 3. and the user didn't set an adapter name, // 3. and the user didn't set an adapter name,
// 4. and the user didn't request the high power pref // 4. and the user didn't request the high power pref
// => don't load the nvidia icd, as it might power on the gpu in hybrid setups causing severe delays // => don't load the nvidia icd, as it might power on the gpu in hybrid setups causing severe delays
#[cfg(wayland_platform)] #[cfg(all(
unix,
feature = "cctk",
not(target_os = "macos"),
not(target_os = "redox")
))]
if !matches!(ids, Some((0x10de, _))) if !matches!(ids, Some((0x10de, _)))
&& std::env::var_os("__NV_PRIME_RENDER_OFFLOAD") && std::env::var_os("__NV_PRIME_RENDER_OFFLOAD")
.is_none_or(|var| var == "0") .is_none_or(|var| var == "0")
@ -112,7 +126,6 @@ impl Compositor {
// if log::max_level() >= log::LevelFilter::Info { // if log::max_level() >= log::LevelFilter::Info {
// let available_adapters: Vec<_> = instance // let available_adapters: Vec<_> = instance
// .enumerate_adapters(settings.backends) // .enumerate_adapters(settings.backends)
// .await
// .iter() // .iter()
// .map(wgpu::Adapter::get_info) // .map(wgpu::Adapter::get_info)
// .collect(); // .collect();
@ -133,11 +146,15 @@ impl Compositor {
let mut adapter = None; let mut adapter = None;
#[cfg_attr(not(unix), allow(dead_code))] #[cfg_attr(not(unix), allow(dead_code))]
if std::env::var_os("WGPU_ADAPTER_NAME").is_none() { if std::env::var_os("WGPU_ADAPTER_NAME").is_none() {
#[cfg(wayland_platform)] #[cfg(all(
unix,
feature = "cctk",
not(target_os = "macos"),
not(target_os = "redox")
))]
if let Some((vendor_id, device_id)) = ids { if let Some((vendor_id, device_id)) = ids {
adapter = instance adapter = instance
.enumerate_adapters(settings.backends) .enumerate_adapters(settings.backends)
.await
.into_iter() .into_iter()
.filter(|adapter| { .filter(|adapter| {
let info = adapter.get_info(); let info = adapter.get_info();
@ -155,7 +172,6 @@ impl Compositor {
} else if let Ok(name) = std::env::var("WGPU_ADAPTER_NAME") { } else if let Ok(name) = std::env::var("WGPU_ADAPTER_NAME") {
adapter = instance adapter = instance
.enumerate_adapters(settings.backends) .enumerate_adapters(settings.backends)
.await
.into_iter() .into_iter()
.filter(|adapter| { .filter(|adapter| {
let info = adapter.get_info(); let info = adapter.get_info();
@ -168,8 +184,6 @@ impl Compositor {
true true
} }
}); });
} else {
adapter = instance.request_adapter(&adapter_options).await.ok();
} }
let adapter = match adapter { let adapter = match adapter {
@ -272,14 +286,6 @@ impl Compositor {
..limits ..limits
}); });
// Request SHADER_F16 only if the adapter supports it (e.g., not available in WebGL2)
let required_features =
if adapter.features().contains(wgpu::Features::SHADER_F16) {
wgpu::Features::SHADER_F16
} else {
wgpu::Features::empty()
};
let mut errors = Vec::new(); let mut errors = Vec::new();
for required_limits in limits { for required_limits in limits {
@ -288,7 +294,7 @@ impl Compositor {
label: Some( label: Some(
"iced_wgpu::window::compositor device descriptor", "iced_wgpu::window::compositor device descriptor",
), ),
required_features, required_features: wgpu::Features::empty(),
required_limits: required_limits.clone(), required_limits: required_limits.clone(),
memory_hints: wgpu::MemoryHints::MemoryUsage, memory_hints: wgpu::MemoryHints::MemoryUsage,
trace: wgpu::Trace::Off, trace: wgpu::Trace::Off,
@ -346,14 +352,6 @@ pub fn present(
) -> Result<(), compositor::SurfaceError> { ) -> Result<(), compositor::SurfaceError> {
match surface.get_current_texture() { match surface.get_current_texture() {
Ok(frame) => { Ok(frame) => {
if frame.texture.width() != viewport.physical_width()
|| frame.texture.height() != viewport.physical_height()
{
return Err(compositor::SurfaceError::Outdated);
}
let suboptimal = frame.suboptimal;
let view = &frame let view = &frame
.texture .texture
.create_view(&wgpu::TextureViewDescriptor::default()); .create_view(&wgpu::TextureViewDescriptor::default());
@ -369,10 +367,6 @@ pub fn present(
on_pre_present(); on_pre_present();
frame.present(); frame.present();
if suboptimal {
return Err(compositor::SurfaceError::Outdated);
}
Ok(()) Ok(())
} }
Err(error) => match error { Err(error) => match error {

View file

@ -1,5 +1,3 @@
#![allow(unexpected_cfgs)]
use std::{ use std::{
fs, fs,
io::{BufRead, BufReader}, io::{BufRead, BufReader},
@ -146,9 +144,7 @@ pub fn get_x11_device_ids<W: Window>(window: &W) -> Option<(u16, u16)> {
{ {
let stat = let stat =
stat(Path::new("/dev/dri").join(device)).ok()?; stat(Path::new("/dev/dri").join(device)).ok()?;
// Hack (u64 as u64) for E2K CPU architecture let dev = stat.st_rdev;
#[allow(clippy::unnecessary_cast)]
let dev = stat.st_rdev as u64;
return super::ids_from_dev(dev); return super::ids_from_dev(dev);
} }
} }
@ -169,9 +165,7 @@ pub fn get_x11_device_ids<W: Window>(window: &W) -> Option<(u16, u16)> {
let dri3 = conn.dri3_open(root, x11rb::NONE).ok()?.reply().ok()?; let dri3 = conn.dri3_open(root, x11rb::NONE).ok()?.reply().ok()?;
let device_fd = dri3.device_fd; let device_fd = dri3.device_fd;
let stat = fstat(device_fd).ok()?; let stat = fstat(device_fd).ok()?;
// Hack (u64 as u64) for E2K CPU architecture let dev = stat.st_rdev;
#[allow(clippy::unnecessary_cast)]
let dev = stat.st_rdev as u64;
super::ids_from_dev(dev) super::ids_from_dev(dev)
} }
} }

View file

@ -29,13 +29,15 @@ highlighter = ["dep:iced_highlighter"]
advanced = [] advanced = []
crisp = [] crisp = []
a11y = ["iced_accessibility"] a11y = ["iced_accessibility"]
wayland = ["iced_runtime/wayland", "dep:cctk"] cctk = ["iced_runtime/cctk", "dep:cctk"]
[dependencies] [dependencies]
iced_renderer.workspace = true iced_renderer.workspace = true
iced_runtime.workspace = true iced_runtime.workspace = true
iced_accessibility.workspace = true iced_accessibility.workspace = true
iced_accessibility.optional = true iced_accessibility.optional = true
cctk.workspace = true
cctk.optional = true
num-traits.workspace = true num-traits.workspace = true
log.workspace = true log.workspace = true
rustc-hash.workspace = true rustc-hash.workspace = true
@ -55,10 +57,3 @@ pulldown-cmark.optional = true
iced_highlighter.workspace = true iced_highlighter.workspace = true
iced_highlighter.optional = true iced_highlighter.optional = true
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
cctk.workspace = true
cctk.optional = true
[build-dependencies]
build_helpers = { path = "../build_helpers" }

View file

@ -1,3 +0,0 @@
fn main() {
build_helpers::cfg_aliases_setup();
}

View file

@ -505,7 +505,6 @@ where
state.raw(), state.raw(),
crate::text::Style { crate::text::Style {
color: style.text_color, color: style.text_color,
..crate::text::Style::default()
}, },
viewport, viewport,
); );

View file

@ -35,7 +35,7 @@ use crate::core::{
color, color,
}; };
use iced_runtime::{Task, task}; use iced_runtime::{Action, Task, task};
/// A widget that aligns its contents inside of its boundaries. /// A widget that aligns its contents inside of its boundaries.
/// ///
@ -601,7 +601,7 @@ pub fn visible_bounds(id: Id) -> Task<Option<Rectangle>> {
operate: &mut dyn FnMut(&mut dyn Operation<Option<Rectangle>>), operate: &mut dyn FnMut(&mut dyn Operation<Option<Rectangle>>),
) { ) {
self.depth += 1; self.depth += 1;
operate(self); self.traverse(operate);
self.depth -= 1; self.depth -= 1;
} }

View file

@ -1,6 +1,7 @@
//! Keyed columns distribute content vertically while keeping continuity. //! Keyed columns distribute content vertically while keeping continuity.
//! Distribute content vertically. //! Distribute content vertically.
use crate::core::event;
use crate::core::layout; use crate::core::layout;
use crate::core::mouse; use crate::core::mouse;
use crate::core::overlay; use crate::core::overlay;
@ -292,12 +293,9 @@ where
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.for_each(|((child, state), c_layout)| { .for_each(|((child, state), c_layout)| {
child.as_widget_mut().operate( child
state, .as_widget_mut()
c_layout.with_virtual_offset(layout.virtual_offset()), .operate(state, c_layout.with_virtual_offset(layout.virtual_offset()), renderer, operation);
renderer,
operation,
);
}); });
}); });
} }
@ -320,13 +318,7 @@ where
.zip(layout.children()) .zip(layout.children())
{ {
child.as_widget_mut().update( child.as_widget_mut().update(
tree, tree, event, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, renderer, clipboard, shell,
event,
c_layout.with_virtual_offset(layout.virtual_offset()),
cursor,
renderer,
clipboard,
shell,
viewport, viewport,
); );
} }
@ -345,13 +337,9 @@ where
.zip(&tree.children) .zip(&tree.children)
.zip(layout.children()) .zip(layout.children())
.map(|((child, tree), c_layout)| { .map(|((child, tree), c_layout)| {
child.as_widget().mouse_interaction( child
tree, .as_widget()
c_layout.with_virtual_offset(layout.virtual_offset()), .mouse_interaction(tree, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, viewport, renderer)
cursor,
viewport,
renderer,
)
}) })
.max() .max()
.unwrap_or_default() .unwrap_or_default()

View file

@ -9,8 +9,6 @@ pub use iced_renderer::graphics;
pub use core::widget::Id; pub use core::widget::Id;
pub(crate) const DRAG_DEADBAND_DISTANCE: f32 = 10.0;
mod action; mod action;
mod mouse_area; mod mouse_area;
mod pin; mod pin;

View file

@ -1,5 +1,5 @@
#![allow(missing_docs)] #![allow(missing_docs)]
use crate::core::event::Event; use crate::core::event::{self, Event};
use crate::core::layout; use crate::core::layout;
use crate::core::mouse; use crate::core::mouse;
use crate::core::overlay; use crate::core::overlay;
@ -363,11 +363,10 @@ where
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
let offset = layout.position() - Point::ORIGIN; let offset = layout.position() - Point::ORIGIN;
for (element, (index, layout, tree)) in self self.visible_elements
.visible_elements
.iter_mut() .iter_mut()
.zip(&mut state.visible_layouts) .zip(&mut state.visible_layouts)
{ .map(|(element, (index, layout, tree))| {
element.as_widget_mut().update( element.as_widget_mut().update(
tree, tree,
event, event,
@ -380,8 +379,8 @@ where
clipboard, clipboard,
shell, shell,
viewport, viewport,
); )
} });
if let Event::Window(window::Event::RedrawRequested(_)) = event { if let Event::Window(window::Event::RedrawRequested(_)) = event {
match &mut state.task { match &mut state.task {

View file

@ -2,6 +2,7 @@
use iced_renderer::core::mouse::Click; use iced_renderer::core::mouse::Click;
use crate::core::event;
use crate::core::layout; use crate::core::layout;
use crate::core::mouse; use crate::core::mouse;
use crate::core::overlay; use crate::core::overlay;

View file

@ -92,8 +92,7 @@ use crate::core::{
}; };
use log::trace; use log::trace;
use crate::DRAG_DEADBAND_DISTANCE; const DRAG_DEADBAND_DISTANCE: f32 = 10.0;
const THICKNESS_RATIO: f32 = 25.0; const THICKNESS_RATIO: f32 = 25.0;
/// A collection of panes distributed using either vertical or horizontal splits /// A collection of panes distributed using either vertical or horizontal splits

View file

@ -6,7 +6,7 @@ use crate::core::renderer;
use crate::core::widget::{self, Tree}; use crate::core::widget::{self, Tree};
use crate::core::{ use crate::core::{
self, Clipboard, Element, Event, Layout, Point, Rectangle, Shell, Size, self, Clipboard, Element, Event, Layout, Point, Rectangle, Shell, Size,
Vector, Vector, event,
}; };
use crate::pane_grid::{Draggable, TitleBar}; use crate::pane_grid::{Draggable, TitleBar};

View file

@ -60,7 +60,6 @@ where
width: Length, width: Length,
height: Length, height: Length,
position: Point, position: Point,
overflow: bool,
} }
impl<'a, Message, Theme, Renderer> Pin<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Pin<'a, Message, Theme, Renderer>
@ -76,7 +75,6 @@ where
width: Length::Fill, width: Length::Fill,
height: Length::Fill, height: Length::Fill,
position: Point::ORIGIN, position: Point::ORIGIN,
overflow: false,
} }
} }
@ -109,12 +107,6 @@ where
self.position.y = y.into().0; self.position.y = y.into().0;
self self
} }
/// Sets whether the [`Pin`] can overflow its boundaries.
pub fn overflow(mut self, overflow: bool) -> Self {
self.overflow = overflow;
self
}
} }
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
@ -154,11 +146,7 @@ where
let limits = limits.width(self.width).height(self.height); let limits = limits.width(self.width).height(self.height);
let available = let available =
if self.overflow { limits.max() - Size::new(self.position.x, self.position.y);
limits.max()
} else {
limits.max() - Size::new(self.position.x, self.position.y)
};
let node = self let node = self
.content .content

View file

@ -459,7 +459,6 @@ where
state.raw(), state.raw(),
crate::text::Style { crate::text::Style {
color: style.text_color, color: style.text_color,
..crate::text::Style::default()
}, },
viewport, viewport,
); );

View file

@ -27,6 +27,7 @@ use iced_runtime::core::widget::Id;
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
use std::borrow::Cow; use std::borrow::Cow;
use crate::core::event;
use crate::core::keyboard; use crate::core::keyboard;
use crate::core::layout; use crate::core::layout;
use crate::core::mouse; use crate::core::mouse;
@ -932,7 +933,7 @@ where
let had_input_method = shell.input_method().is_enabled(); let had_input_method = shell.input_method().is_enabled();
let c_event = match event.clone() { let mut c_event = match event.clone() {
Event::Dnd(dnd::DndEvent::Offer( Event::Dnd(dnd::DndEvent::Offer(
id, id,
dnd::OfferEvent::Enter { dnd::OfferEvent::Enter {
@ -960,16 +961,6 @@ where
y: y + translation.y as f64, y: y + translation.y as f64,
}, },
)), )),
Event::Touch(touch::Event::FingerLifted {
id,
position,
}) if matches!(
state.interaction,
Interaction::TouchScrolling(_)
) =>
{
Event::Touch(touch::Event::FingerLost { id, position })
}
e => e, e => e,
}; };
self.content.as_widget_mut().update( self.content.as_widget_mut().update(
@ -997,14 +988,6 @@ where
} }
}; };
if matches!(
event,
Event::Mouse(mouse::Event::CursorMoved { .. })
| Event::Touch(touch::Event::FingerPressed { .. })
) {
state.suppress_touch_hover = false;
}
if matches!( if matches!(
event, event,
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
@ -1013,18 +996,10 @@ where
| touch::Event::FingerLost { .. } | touch::Event::FingerLost { .. }
) )
) { ) {
if matches!(state.interaction, Interaction::TouchScrolling(_)) {
state.suppress_touch_hover = true;
}
state.interaction = Interaction::None; state.interaction = Interaction::None;
state.touch_press_start = None;
return; return;
} }
if let Event::Touch(touch::Event::FingerPressed { .. }) = event {
state.touch_press_start = cursor_over_scrollable;
}
if shell.is_event_captured() { if shell.is_event_captured() {
return; return;
} }
@ -1118,28 +1093,6 @@ where
Interaction::TouchScrolling(position); Interaction::TouchScrolling(position);
} }
touch::Event::FingerMoved { .. } => { touch::Event::FingerMoved { .. } => {
let Some(cursor_position) = cursor.position()
else {
return;
};
if !matches!(
state.interaction,
Interaction::TouchScrolling(_)
) {
let Some(start) = state.touch_press_start
else {
return;
};
if start.distance(cursor_position)
< crate::DRAG_DEADBAND_DISTANCE
{
return;
}
state.interaction =
Interaction::TouchScrolling(start);
}
let Interaction::TouchScrolling( let Interaction::TouchScrolling(
scroll_box_touched_at, scroll_box_touched_at,
) = state.interaction ) = state.interaction
@ -1147,6 +1100,11 @@ where
return; return;
}; };
let Some(cursor_position) = cursor.position()
else {
return;
};
let delta = Vector::new( let delta = Vector::new(
scroll_box_touched_at.x - cursor_position.x, scroll_box_touched_at.x - cursor_position.x,
scroll_box_touched_at.y - cursor_position.y, scroll_box_touched_at.y - cursor_position.y,
@ -1364,14 +1322,6 @@ where
state.translation(self.direction, bounds, content_bounds); state.translation(self.direction, bounds, content_bounds);
let cursor = match cursor_over_scrollable { let cursor = match cursor_over_scrollable {
_ if state.suppress_touch_hover
|| matches!(
state.interaction,
Interaction::TouchScrolling(_)
) =>
{
mouse::Cursor::Unavailable
}
Some(cursor_position) Some(cursor_position)
if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) => if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) =>
{ {
@ -1547,14 +1497,6 @@ where
state.translation(self.direction, bounds, content_bounds); state.translation(self.direction, bounds, content_bounds);
let cursor = match cursor_over_scrollable { let cursor = match cursor_over_scrollable {
_ if state.suppress_touch_hover
|| matches!(
state.interaction,
Interaction::TouchScrolling(_)
) =>
{
cursor.levitate() + translation
}
Some(cursor_position) Some(cursor_position)
if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) => if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) =>
{ {
@ -2080,8 +2022,6 @@ struct State {
last_notified: Option<Viewport>, last_notified: Option<Viewport>,
last_scrolled: Option<Instant>, last_scrolled: Option<Instant>,
is_scrollbar_visible: bool, is_scrollbar_visible: bool,
touch_press_start: Option<Point>,
suppress_touch_hover: bool,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -2107,8 +2047,6 @@ impl Default for State {
last_notified: None, last_notified: None,
last_scrolled: None, last_scrolled: None,
is_scrollbar_visible: true, is_scrollbar_visible: true,
touch_press_start: None,
suppress_touch_hover: false,
} }
} }
} }

View file

@ -110,8 +110,6 @@ where
height: f32, height: f32,
class: Theme::Class<'a>, class: Theme::Class<'a>,
status: Option<Status>, status: Option<Status>,
handle_width: f32,
handle_height: f32,
} }
impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme> impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme>
@ -121,13 +119,7 @@ where
Theme: Catalog, Theme: Catalog,
{ {
/// The default height of a [`Slider`]. /// The default height of a [`Slider`].
pub const DEFAULT_HEIGHT: f32 = 26.0; pub const DEFAULT_HEIGHT: f32 = 16.0;
/// The default max height of a [`Handle`].
pub const DEFAULT_MAX_HANDLE_HEIGHT: f32 = 26.0;
/// The default max width of a [`Handle`].
pub const DEFAULT_MAX_HANDLE_WIDTH: f32 = 26.0;
/// Creates a new [`Slider`]. /// Creates a new [`Slider`].
/// ///
@ -173,8 +165,6 @@ where
height: Self::DEFAULT_HEIGHT, height: Self::DEFAULT_HEIGHT,
class: Theme::default(), class: Theme::default(),
status: None, status: None,
handle_width: Self::DEFAULT_MAX_HANDLE_WIDTH,
handle_height: Self::DEFAULT_MAX_HANDLE_HEIGHT,
} }
} }
@ -217,24 +207,6 @@ where
self self
} }
/// Sets the max width of the [`Handle`].
///
/// Theme can be used to change the size and shape of handle
/// but width of handle defined in theme will not exceed this width
pub fn handle_width(mut self, width: impl Into<Pixels>) -> Self {
self.handle_width = width.into().0;
self
}
/// Sets the max height of the [`Handle`].
///
/// Theme can be used to change the size and shape of handle
/// but height of handle defined in theme will not exceed this height
pub fn handle_height(mut self, height: impl Into<Pixels>) -> Self {
self.handle_height = height.into().0;
self
}
/// Sets the step size of the [`Slider`]. /// Sets the step size of the [`Slider`].
pub fn step(mut self, step: impl Into<T>) -> Self { pub fn step(mut self, step: impl Into<T>) -> Self {
self.step = step.into(); self.step = step.into();
@ -351,15 +323,8 @@ where
let mut update = || { let mut update = || {
let current_value = self.value; let current_value = self.value;
self.handle_width = self.handle_width.min(layout.bounds().width);
self.handle_height = self.handle_height.min(layout.bounds().height);
let locate = |cursor_position: Point| -> Option<T> { let locate = |cursor_position: Point| -> Option<T> {
let bounds = Rectangle { let bounds = layout.bounds();
x: layout.bounds().x + self.handle_width / 2.0,
width: layout.bounds().width - self.handle_width,
..layout.bounds()
};
if cursor_position.x <= bounds.x { if cursor_position.x <= bounds.x {
Some(*self.range.start()) Some(*self.range.start())
@ -544,29 +509,53 @@ where
_cursor: mouse::Cursor, _cursor: mouse::Cursor,
_viewport: &Rectangle, _viewport: &Rectangle,
) { ) {
let bounds = Rectangle { let bounds = layout.bounds();
x: layout.bounds().x + self.handle_width / 2.0,
width: layout.bounds().width - self.handle_width,
..layout.bounds()
};
let style = let style =
theme.style(&self.class, self.status.unwrap_or(Status::Active)); theme.style(&self.class, self.status.unwrap_or(Status::Active));
let (mut handle_width, mut handle_height, handle_border_radius) = let border_width = style
.handle
.border_width
.min(bounds.height / 2.0)
.min(bounds.width / 2.0);
let (handle_width, handle_height, handle_border_radius) =
match style.handle.shape { match style.handle.shape {
HandleShape::Circle { radius } => { HandleShape::Circle { radius } => {
let radius = (radius)
.max(2.0 * border_width)
.min(bounds.height / 2.0)
.min(bounds.width / 2.0 + 2.0 * border_width);
(radius * 2.0, radius * 2.0, Radius::from(radius)) (radius * 2.0, radius * 2.0, Radius::from(radius))
} }
HandleShape::Rectangle { HandleShape::Rectangle {
height, height,
width, width,
border_radius, border_radius,
} => (f32::from(width), f32::from(height), border_radius), } => {
}; let width = (f32::from(width)).max(2.0 * border_width);
let height = (f32::from(height)).max(2.0 * border_width);
let mut border_radius: [f32; 4] = border_radius.into();
for r in &mut border_radius {
*r = (*r)
.min(height / 2.0)
.min(width / 2.0)
.max(*r * (width + border_width * 2.0) / width);
}
handle_width = handle_width.min(self.handle_width); (
handle_height = handle_height.min(self.handle_height); width,
height,
Radius {
top_left: border_radius[0],
top_right: border_radius[1],
bottom_right: border_radius[2],
bottom_left: border_radius[3],
},
)
}
};
let value = self.value.into() as f32; let value = self.value.into() as f32;
let (range_start, range_end) = { let (range_start, range_end) = {
@ -578,7 +567,8 @@ where
let offset = if range_start >= range_end { let offset = if range_start >= range_end {
0.0 0.0
} else { } else {
bounds.width * (value - range_start) / (range_end - range_start) (bounds.width - handle_width) * (value - range_start)
/ (range_end - range_start)
}; };
let rail_y = bounds.y + bounds.height / 2.0; let rail_y = bounds.y + bounds.height / 2.0;
@ -600,8 +590,7 @@ where
x: bounds.x + offset, x: bounds.x + offset,
y: rail_y + 6.0, y: rail_y + 6.0,
width: BREAKPOINT_WIDTH, width: BREAKPOINT_WIDTH,
height: 8.0_f32 height: 8.0,
.min(bounds.y + bounds.height - (rail_y + 6.0)),
}, },
border: Border { border: Border {
radius: 0.0.into(), radius: 0.0.into(),
@ -618,9 +607,9 @@ where
renderer::Quad { renderer::Quad {
bounds: Rectangle { bounds: Rectangle {
x: bounds.x, x: bounds.x,
y: rail_y - (style.rail.width).min(bounds.height) / 2.0, y: rail_y - style.rail.width / 2.0,
width: offset, width: offset + handle_width / 2.0,
height: (style.rail.width).min(bounds.height), height: style.rail.width,
}, },
border: style.rail.border, border: style.rail.border,
..renderer::Quad::default() ..renderer::Quad::default()
@ -633,10 +622,10 @@ where
renderer.fill_quad( renderer.fill_quad(
renderer::Quad { renderer::Quad {
bounds: Rectangle { bounds: Rectangle {
x: bounds.x + offset, x: bounds.x + offset + handle_width / 2.0,
y: rail_y - (style.rail.width).min(bounds.height) / 2.0, y: rail_y - style.rail.width / 2.0,
width: bounds.width - offset, width: bounds.width - offset - handle_width / 2.0,
height: (style.rail.width).min(bounds.height), height: style.rail.width,
}, },
border: style.rail.border, border: style.rail.border,
..renderer::Quad::default() ..renderer::Quad::default()
@ -648,7 +637,7 @@ where
renderer.fill_quad( renderer.fill_quad(
renderer::Quad { renderer::Quad {
bounds: Rectangle { bounds: Rectangle {
x: bounds.x + offset - handle_width / 2.0, x: bounds.x + offset,
y: rail_y - (handle_height / 2.0), y: rail_y - (handle_height / 2.0),
width: handle_width, width: handle_width,
height: handle_height, height: handle_height,

View file

@ -1,5 +1,6 @@
//! Display content on top of other content. //! Display content on top of other content.
use crate::core::event;
use crate::core::layout; use crate::core::layout;
use crate::core::mouse; use crate::core::mouse;
use crate::core::overlay; use crate::core::overlay;

View file

@ -179,10 +179,7 @@ where
{ {
let color = color.map(Into::into); let color = color.map(Into::into);
self.style(move |_theme| Style { self.style(move |_theme| Style { color })
color,
..Style::default()
})
} }
/// Sets the default style class of the [`Rich`] text. /// Sets the default style class of the [`Rich`] text.

View file

@ -544,8 +544,6 @@ pub struct State<Highlighter: text::Highlighter> {
highlighter: RefCell<Highlighter>, highlighter: RefCell<Highlighter>,
highlighter_settings: Highlighter::Settings, highlighter_settings: Highlighter::Settings,
highlighter_format_address: usize, highlighter_format_address: usize,
context_menu_position: Option<Point>,
pending_edit: Option<Action>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -581,14 +579,6 @@ impl<Highlighter: text::Highlighter> State<Highlighter> {
pub fn is_focused(&self) -> bool { pub fn is_focused(&self) -> bool {
self.focus.is_some() self.focus.is_some()
} }
/// Clears focus, selection, and all interaction state.
pub fn clear_focus(&mut self) {
self.focus = None;
self.drag_click = None;
self.context_menu_position = None;
self.pending_edit = Some(Action::ClearSelection);
}
} }
impl<Highlighter: text::Highlighter> operation::Focusable impl<Highlighter: text::Highlighter> operation::Focusable
@ -603,7 +593,7 @@ impl<Highlighter: text::Highlighter> operation::Focusable
} }
fn unfocus(&mut self) { fn unfocus(&mut self) {
self.clear_focus(); self.focus = None;
} }
} }
@ -631,8 +621,6 @@ where
)), )),
highlighter_settings: self.highlighter_settings.clone(), highlighter_settings: self.highlighter_settings.clone(),
highlighter_format_address: self.highlighter_format as usize, highlighter_format_address: self.highlighter_format as usize,
context_menu_position: None,
pending_edit: None,
}) })
} }
@ -711,58 +699,6 @@ where
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
_viewport: &Rectangle, _viewport: &Rectangle,
) { ) {
// Handle context menu and focus-loss on clicks outside.
{
let state = tree.state.downcast_mut::<State<Highlighter>>();
let text_bounds = layout.bounds();
if matches!(
event,
Event::Mouse(mouse::Event::ButtonPressed(_))
| Event::Touch(
crate::core::touch::Event::FingerPressed { .. }
)
) && cursor.position_over(text_bounds).is_none()
{
state.clear_focus();
}
match event {
Event::Mouse(mouse::Event::ButtonPressed(
mouse::Button::Right,
)) => {
if let Some(pos) = cursor.position_over(text_bounds) {
if state.focus.is_none() {
state.focus = Some(Focus::now());
}
state.context_menu_position = Some(pos);
shell.capture_event();
return;
}
}
Event::Mouse(mouse::Event::ButtonPressed(
mouse::Button::Left,
)) => {
if state.context_menu_position.take().is_some() {
shell.capture_event();
return;
}
}
_ => {}
}
}
// Process pending edits from context menu
// publishes via on_edit so the app calls content.perform().
{
let state = tree.state.downcast_mut::<State<Highlighter>>();
if let Some(action) = state.pending_edit.take() {
if let Some(on_edit) = self.on_edit.as_ref() {
shell.publish((on_edit)(action));
}
}
}
let Some(on_edit) = self.on_edit.as_ref() else { let Some(on_edit) = self.on_edit.as_ref() else {
return; return;
}; };
@ -1287,23 +1223,6 @@ impl<Message> Binding<Message> {
return Some(binding); return Some(binding);
} }
let alt_clipboard = match key.as_ref() {
keyboard::Key::Named(key::Named::Insert) if modifiers.shift() => {
Some(Self::Paste)
}
keyboard::Key::Named(key::Named::Insert) if modifiers.command() => {
Some(Self::Copy)
}
keyboard::Key::Named(key::Named::Delete) if modifiers.shift() => {
Some(Self::Cut)
}
_ => None,
};
if let Some(binding) = alt_clipboard {
return Some(binding);
}
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let modified_key = let modified_key =
convert_macos_shortcut(&key, modifiers).unwrap_or(modified_key); convert_macos_shortcut(&key, modifiers).unwrap_or(modified_key);
@ -1626,63 +1545,3 @@ pub(crate) fn convert_macos_shortcut(
Some(keyboard::Key::Named(key)) Some(keyboard::Key::Named(key))
} }
use crate::core::widget::text::HasSelectableText;
impl<Highlighter, Message, Theme, Renderer> HasSelectableText
for TextEditor<'_, Highlighter, Message, Theme, Renderer>
where
Highlighter: text::Highlighter,
Theme: Catalog,
Renderer: text::Renderer,
{
fn selected_text(&self, _tree: &widget::Tree) -> Option<String> {
self.content.selection()
}
fn select_all(&self, tree: &mut widget::Tree) {
let state = tree.state.downcast_mut::<State<Highlighter>>();
state.pending_edit = Some(Action::SelectAll);
}
fn is_editable(&self) -> bool {
self.on_edit.is_some()
}
fn is_focused(&self, tree: &widget::Tree) -> bool {
tree.state.downcast_ref::<State<Highlighter>>().is_focused()
}
fn context_menu_position(&self, tree: &widget::Tree) -> Option<Point> {
tree.state
.downcast_ref::<State<Highlighter>>()
.context_menu_position
}
fn set_context_menu_position(
&self,
tree: &mut widget::Tree,
pos: Option<Point>,
) {
tree.state
.downcast_mut::<State<Highlighter>>()
.context_menu_position = pos;
}
fn delete_selection(&self, tree: &mut widget::Tree) -> Option<String> {
let state = tree.state.downcast_mut::<State<Highlighter>>();
state.pending_edit = Some(Action::Edit(Edit::Delete));
Some(String::new())
}
fn paste_text(
&self,
tree: &mut widget::Tree,
text: &str,
) -> Option<String> {
let state = tree.state.downcast_mut::<State<Highlighter>>();
state.pending_edit =
Some(Action::Edit(Edit::Paste(Arc::new(text.to_owned()))));
Some(String::new())
}
}

View file

@ -1,5 +1,6 @@
use crate::container; use crate::container;
use crate::core::event;
use crate::core::layout; use crate::core::layout;
use crate::core::mouse; use crate::core::mouse;
use crate::core::overlay; use crate::core::overlay;

View file

@ -37,6 +37,7 @@ use std::borrow::Cow;
use iced_runtime::core::border::Radius; use iced_runtime::core::border::Radius;
use crate::core::alignment; use crate::core::alignment;
use crate::core::border;
use crate::core::layout; use crate::core::layout;
use crate::core::mouse; use crate::core::mouse;
use crate::core::renderer; use crate::core::renderer;
@ -482,7 +483,6 @@ where
state.raw(), state.raw(),
crate::text::Style { crate::text::Style {
color: style.text_color, color: style.text_color,
..crate::text::Style::default()
}, },
viewport, viewport,
); );
@ -509,7 +509,7 @@ where
let space = style.handle_margin; let space = style.handle_margin;
let _toggler_background_bounds = Rectangle { let toggler_background_bounds = Rectangle {
x: bounds.x, x: bounds.x,
y: bounds.y, y: bounds.y,
width: bounds.width, width: bounds.width,
@ -529,7 +529,7 @@ where
style.background, style.background,
); );
let _padding = (style.padding_ratio * bounds.height).round(); let padding = (style.padding_ratio * bounds.height).round();
let toggler_foreground_bounds = Rectangle { let toggler_foreground_bounds = Rectangle {
x: bounds.x x: bounds.x
+ if self.is_toggled { + if self.is_toggled {

View file

@ -14,18 +14,7 @@ keywords.workspace = true
workspace = true workspace = true
[features] [features]
wayland = [ wayland = ["winit/wayland", "wayland-csd-adwaita"]
"winit/wayland",
"wayland-csd-adwaita",
"wayland-protocols",
"raw-window-handle",
"iced_runtime/wayland",
"wayland-backend",
"xkbcommon",
"xkbcommon-dl",
"xkeysym",
"dep:cctk",
]
default = ["x11", "wayland-dlopen"] default = ["x11", "wayland-dlopen"]
debug = ["iced_debug/enable"] debug = ["iced_debug/enable"]
sysinfo = ["dep:sysinfo"] sysinfo = ["dep:sysinfo"]
@ -37,6 +26,17 @@ program = []
wayland-dlopen = ["winit/wayland-dlopen"] wayland-dlopen = ["winit/wayland-dlopen"]
wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] wayland-csd-adwaita = ["winit/wayland-csd-adwaita"]
a11y = ["iced_accessibility", "iced_runtime/a11y"] a11y = ["iced_accessibility", "iced_runtime/a11y"]
cctk = [
"wayland",
"wayland-protocols",
"raw-window-handle",
"iced_runtime/cctk",
"wayland-backend",
"xkbcommon",
"xkbcommon-dl",
"xkeysym",
"dep:cctk",
]
# Prevents multiple separate process instances. # Prevents multiple separate process instances.
single-instance = [] single-instance = []
@ -64,8 +64,7 @@ iced_debug.workspace = true
iced_program.workspace = true iced_program.workspace = true
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
mundy.workspace = true mundy.workspace = true
mundy.optional = true mundy.optional = true
@ -90,6 +89,3 @@ winapi.workspace = true
web-sys.workspace = true web-sys.workspace = true
web-sys.features = ["Document", "Window", "HtmlCanvasElement"] web-sys.features = ["Document", "Window", "HtmlCanvasElement"]
wasm-bindgen-futures.workspace = true wasm-bindgen-futures.workspace = true
[build-dependencies]
build_helpers = { path = "../build_helpers" }

View file

@ -1,3 +0,0 @@
fn main() {
build_helpers::cfg_aliases_setup();
}

View file

@ -49,10 +49,7 @@ pub fn event_func(
position, position,
.. ..
} => { } => {
let resizable = !window.is_decorated() if !window.is_decorated() {
&& !window.is_maximized()
&& window.fullscreen().is_none();
if resizable {
let location = cursor_resize_direction( let location = cursor_resize_direction(
window.surface_size(), window.surface_size(),
*position, *position,

View file

@ -92,7 +92,7 @@ impl Clipboard {
}) })
.unwrap_or(State::Unavailable); .unwrap_or(State::Unavailable);
#[cfg(wayland_platform)] #[cfg(target_os = "linux")]
if let State::Connected { clipboard, .. } = &state { if let State::Connected { clipboard, .. } = &state {
clipboard.init_dnd(Box::new(sender)); clipboard.init_dnd(Box::new(sender));
} }

View file

@ -23,33 +23,6 @@ use winit::event::Force;
use winit::icon::IconProvider; use winit::icon::IconProvider;
use winit::keyboard::SmolStr; use winit::keyboard::SmolStr;
/// Returns the activation token provided by the compositor, if any.
///
/// The token is single-use, so it is only ever handed out to the first window.
#[cfg(target_os = "linux")]
fn take_activation_token() -> Option<winit::window::ActivationToken> {
use std::sync::Mutex;
use std::sync::OnceLock;
use winit::platform::startup_notify::reset_activation_token_env;
static TOKEN: OnceLock<Mutex<Option<winit::window::ActivationToken>>> =
OnceLock::new();
TOKEN
.get_or_init(|| {
let token = std::env::var("XDG_ACTIVATION_TOKEN")
.ok()
.map(winit::window::ActivationToken::from_raw);
reset_activation_token_env();
Mutex::new(token)
})
.lock()
.ok()
.and_then(|mut token| token.take())
}
/// Converts some [`window::Settings`] into some `WindowAttributes` from `winit`. /// Converts some [`window::Settings`] into some `WindowAttributes` from `winit`.
pub fn window_attributes( pub fn window_attributes(
settings: window::Settings, settings: window::Settings,
@ -209,19 +182,12 @@ pub fn window_attributes(
{ {
use winit::platform::wayland::WindowAttributesWayland; use winit::platform::wayland::WindowAttributesWayland;
let mut wayland_attributes = WindowAttributesWayland::default() attributes = attributes.with_platform_attributes(Box::new(
.with_name( WindowAttributesWayland::default().with_name(
&settings.platform_specific.application_id, &settings.platform_specific.application_id,
&settings.platform_specific.application_id, &settings.platform_specific.application_id,
); ),
));
if let Some(token) = take_activation_token() {
wayland_attributes =
wayland_attributes.with_activation_token(token);
}
attributes = attributes
.with_platform_attributes(Box::new(wayland_attributes));
} }
} }
@ -421,19 +387,19 @@ pub fn window_event(
self::modifiers(new_modifiers.state()), self::modifiers(new_modifiers.state()),
))) )))
} }
WindowEvent::Ime(event) => match event { WindowEvent::Ime(event) => Some(Event::InputMethod(match event {
Ime::Enabled => Some(input_method::Event::Opened), Ime::Enabled => input_method::Event::Opened,
Ime::Preedit(content, size) => Some(input_method::Event::Preedit( Ime::Preedit(content, size) => input_method::Event::Preedit(
content, content,
size.map(|(start, end)| start..end), size.map(|(start, end)| start..end),
)), ),
Ime::Commit(content) => Some(input_method::Event::Commit(content)), Ime::Commit(content) => input_method::Event::Commit(content),
Ime::Disabled => Some(input_method::Event::Closed), Ime::Disabled => input_method::Event::Closed,
// Upstream iced depends on the winit version which doesn't support Ime::DeleteSurrounding {
// this kind of ime event. before_bytes,
Ime::DeleteSurrounding { .. } => None, after_bytes,
} } => todo!(),
.map(Event::InputMethod), })),
WindowEvent::Focused(focused) => Some(Event::Window(if focused { WindowEvent::Focused(focused) => Some(Event::Window(if focused {
window::Event::Focused window::Event::Focused
} else { } else {
@ -458,7 +424,7 @@ pub fn window_event(
WindowEvent::ScaleFactorChanged { scale_factor, .. } => { WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
Some(Event::Window(window::Event::Rescaled(scale_factor as f32))) Some(Event::Window(window::Event::Rescaled(scale_factor as f32)))
} }
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
WindowEvent::SuggestedBounds(bounds) => { WindowEvent::SuggestedBounds(bounds) => {
let size = bounds.map(|bounds| { let size = bounds.map(|bounds| {
let size = bounds.to_logical(scale_factor); let size = bounds.to_logical(scale_factor);
@ -471,7 +437,7 @@ pub fn window_event(
), ),
))) )))
} }
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
WindowEvent::WindowStateChanged => { WindowEvent::WindowStateChanged => {
use cctk::sctk::reexports::csd_frame::WindowState; use cctk::sctk::reexports::csd_frame::WindowState;
use winit::platform::wayland::WindowExtWayland; use winit::platform::wayland::WindowExtWayland;
@ -1525,7 +1491,7 @@ pub fn winit_key_code(
}) })
} }
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
fn winit_native_key_code( fn winit_native_key_code(
keycode: keyboard::key::NativeCode, keycode: keyboard::key::NativeCode,
) -> winit::keyboard::NativeKeyCode { ) -> winit::keyboard::NativeKeyCode {
@ -1549,7 +1515,7 @@ fn winit_native_key_code(
} }
/// Reconstruct the raw keycode /// Reconstruct the raw keycode
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
pub fn physical_to_scancode(physical: keyboard::key::Physical) -> Option<u32> { pub fn physical_to_scancode(physical: keyboard::key::Physical) -> Option<u32> {
let Some(physical_key) = (match physical { let Some(physical_key) = (match physical {
keyboard::key::Physical::Code(code) => { keyboard::key::Physical::Code(code) => {

View file

@ -40,6 +40,12 @@ pub mod clipboard;
pub mod conversion; pub mod conversion;
pub mod platform_specific; pub mod platform_specific;
#[cfg(feature = "program")]
pub mod program;
#[cfg(feature = "system")]
pub mod system;
mod error; mod error;
mod proxy; mod proxy;
mod window; mod window;
@ -47,6 +53,7 @@ mod window;
pub use clipboard::Clipboard; pub use clipboard::Clipboard;
pub use error::Error; pub use error::Error;
pub use proxy::Proxy; pub use proxy::Proxy;
use winit::dpi::LogicalSize;
use winit::dpi::PhysicalPosition; use winit::dpi::PhysicalPosition;
use winit::dpi::PhysicalSize; use winit::dpi::PhysicalSize;
@ -91,10 +98,10 @@ where
let event_loop = EventLoop::new().expect("Create event loop"); let event_loop = EventLoop::new().expect("Create event loop");
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
let is_wayland = let is_wayland =
winit::platform::wayland::EventLoopExtWayland::is_wayland(&event_loop); winit::platform::wayland::EventLoopExtWayland::is_wayland(&event_loop);
#[cfg(not(wayland_platform))] #[cfg(not(all(feature = "cctk", target_os = "linux")))]
let is_wayland = false; let is_wayland = false;
// TODO this is new.. // TODO this is new..
@ -185,14 +192,10 @@ where
receiver: mpsc::UnboundedReceiver<Control>, receiver: mpsc::UnboundedReceiver<Control>,
error: Option<Error>, error: Option<Error>,
system_theme: Option<oneshot::Sender<theme::Mode>>, system_theme: Option<oneshot::Sender<theme::Mode>>,
#[cfg(feature = "a11y")]
control_sender: mpsc::UnboundedSender<Control>, control_sender: mpsc::UnboundedSender<Control>,
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
adapters: std::collections::HashMap< adapters: std::collections::HashMap<window::Id, (u64, iced_accessibility::accesskit_winit::Adapter)>,
window::Id,
(u64, iced_accessibility::accesskit_winit::Adapter),
>,
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
is_booted: std::rc::Rc<std::cell::RefCell<bool>>, is_booted: std::rc::Rc<std::cell::RefCell<bool>>,
@ -212,7 +215,6 @@ where
id: settings.id, id: settings.id,
sender: event_sender, sender: event_sender,
receiver: control_receiver, receiver: control_receiver,
#[cfg(feature = "a11y")]
control_sender: control_sender.clone(), control_sender: control_sender.clone(),
error: None, error: None,
system_theme: Some(system_theme_sender), system_theme: Some(system_theme_sender),
@ -261,7 +263,7 @@ where
| winit::event::WindowEvent::Moved(_) | winit::event::WindowEvent::Moved(_)
); );
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
{ {
if matches!(event, WindowEvent::RedrawRequested) { if matches!(event, WindowEvent::RedrawRequested) {
for id in for id in
@ -499,15 +501,9 @@ where
} }
}; };
} }
let window: Arc< let window: Arc<dyn winit::window::Window + 'static> = Arc::from(window);
dyn winit::window::Window + 'static,
> = Arc::from(window);
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
self.init_adapter( self.init_adapter(event_loop, id, window.clone());
event_loop,
id,
window.clone(),
);
self.process_event( self.process_event(
event_loop, event_loop,
@ -576,10 +572,11 @@ where
.expect("Send event"); .expect("Send event");
} }
Control::Winit(id, e) => { Control::Winit(id, e) => {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
{ {
if matches!(e, WindowEvent::RedrawRequested) if matches!(e, WindowEvent::RedrawRequested)
{ {
for id in crate::subsurface_widget::subsurface_ids(id) { for id in crate::subsurface_widget::subsurface_ids(id) {
_ = self.sender _ = self.sender
.unbounded_send(Event::Winit( .unbounded_send(Event::Winit(
@ -622,7 +619,7 @@ where
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
Control::Cleanup(id) => { Control::Cleanup(id) => {
_ = self.adapters.remove(&id); _ = self.adapters.remove(&id);
} },
}, },
_ => { _ => {
break; break;
@ -637,21 +634,19 @@ where
} }
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
fn init_adapter( fn init_adapter(&mut self, event_loop: &(dyn winit::event_loop::ActiveEventLoop + 'static), id: core::window::Id, window: Arc<dyn winit::window::Window + 'static>) {
&mut self,
event_loop: &(dyn winit::event_loop::ActiveEventLoop + 'static),
id: core::window::Id,
window: Arc<dyn winit::window::Window + 'static>,
) {
use crate::a11y::*; use crate::a11y::*;
use iced_accessibility::accesskit::{ use iced_accessibility::accesskit::{
ActivationHandler, Node, NodeId, Role, Tree, TreeUpdate, ActivationHandler, Node, NodeId, Role,
Tree, TreeUpdate,
}; };
use iced_accessibility::accesskit_winit::Adapter; use iced_accessibility::accesskit_winit::Adapter;
let node_id = iced_runtime::core::id::window_node_id(); let node_id =
iced_runtime::core::id::window_node_id();
let activation_handler = WinitActivationHandler { let activation_handler =
WinitActivationHandler {
proxy: self.control_sender.clone(), proxy: self.control_sender.clone(),
title: String::new(), title: String::new(),
}; };
@ -661,7 +656,8 @@ where
proxy: self.control_sender.clone(), proxy: self.control_sender.clone(),
}; };
let deactivation_handler = WinitDeactivationHandler { let deactivation_handler =
WinitDeactivationHandler {
proxy: self.control_sender.clone(), proxy: self.control_sender.clone(),
}; };
@ -678,6 +674,8 @@ where
), ),
), ),
); );
} }
} }
@ -772,7 +770,7 @@ async fn run_instance<P>(
let mut platform_specific_handler = let mut platform_specific_handler =
crate::platform_specific::PlatformSpecific::default(); crate::platform_specific::PlatformSpecific::default();
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
if is_wayland { if is_wayland {
platform_specific_handler = platform_specific_handler.with_wayland( platform_specific_handler = platform_specific_handler.with_wayland(
control_sender.clone(), control_sender.clone(),
@ -840,30 +838,6 @@ async fn run_instance<P>(
log::info!("System theme: {system_theme:?}"); log::info!("System theme: {system_theme:?}");
macro_rules! run_action {
($action:expr) => {
run_action(
$action,
&program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
&mut dnd_surface,
&mut dnd_surface_id,
is_daemon,
)
};
}
'next_event: loop { 'next_event: loop {
// Empty the queue if possible // Empty the queue if possible
let event = if let Ok(event) = event_receiver.try_recv() { let event = if let Ok(event) = event_receiver.try_recv() {
@ -883,13 +857,13 @@ async fn run_instance<P>(
exit_on_close_request, exit_on_close_request,
make_visible, make_visible,
on_open, on_open,
resize_border, resize_border
} => { } => {
#[cfg(wayland_platform)]
#[cfg(all(feature = "cctk", target_os = "linux"))]
platform_specific_handler.send_wayland( platform_specific_handler.send_wayland(
platform_specific::Action::TrackWindow(window.clone(), id), platform_specific::Action::TrackWindow(window.clone(), id),
); );
if compositor.is_none() {
match create_compositor::<P>( match create_compositor::<P>(
window.clone(), window.clone(),
CreateCompositor { CreateCompositor {
@ -914,7 +888,6 @@ async fn run_instance<P>(
continue; continue;
} }
} }
}
let window_theme = window let window_theme = window
.theme() .theme()
@ -940,7 +913,7 @@ async fn run_instance<P>(
compositor, compositor,
exit_on_close_request, exit_on_close_request,
system_theme, system_theme,
resize_border, resize_border
); );
window.raw.set_theme(conversion::window_theme( window.raw.set_theme(conversion::window_theme(
@ -1026,7 +999,22 @@ async fn run_instance<P>(
} }
} }
Event::UserEvent(action) => { Event::UserEvent(action) => {
let exited = run_action!(action); let exited = run_action(
action,
&program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
);
if exited { if exited {
runtime.track(None.into_iter()); runtime.track(None.into_iter());
} }
@ -1054,10 +1042,10 @@ async fn run_instance<P>(
continue; continue;
} }
// XX must force update to corner radius before the surface is committed. // XX must force update to corner radius before the surface is committed.
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
if (window.surface_version != window.state.surface_version() if (window.surface_version != window.state.surface_version()
|| window.logical_size() != window.state.logical_size()) || window.logical_size() != window.state.logical_size()
&& !crate::subsurface_widget::is_subsurface(window_id) ) && !crate::subsurface_widget::is_subsurface(window_id)
{ {
platform_specific_handler.send_wayland( platform_specific_handler.send_wayland(
platform_specific::Action::ResizeWindow(id), platform_specific::Action::ResizeWindow(id),
@ -1168,7 +1156,22 @@ async fn run_instance<P>(
continue; continue;
} }
_ = run_action!(action); _ = run_action(
action,
&program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
);
} }
for (window_id, window) in window_manager.iter_mut() { for (window_id, window) in window_manager.iter_mut() {
@ -1323,7 +1326,9 @@ async fn run_instance<P>(
continue; continue;
}; };
// Initiates a drag resize window state when found. // Initiates a drag resize window state when found.
if let Some(func) = window.drag_resize_window_func.as_mut() { if let Some(func) =
window.drag_resize_window_func.as_mut()
{
if func(window.raw.as_ref(), &event) { if func(window.raw.as_ref(), &event) {
continue; continue;
} }
@ -1349,9 +1354,22 @@ async fn run_instance<P>(
if matches!(event, winit::event::WindowEvent::CloseRequested) if matches!(event, winit::event::WindowEvent::CloseRequested)
&& window.exit_on_close_request && window.exit_on_close_request
{ {
_ = run_action!(Action::Window( _ = run_action(
runtime::window::Action::Close(id) Action::Window(runtime::window::Action::Close(id)),
)); &program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
);
} else { } else {
window.state.update(&program, window.raw.as_ref(), &event); window.state.update(&program, window.raw.as_ref(), &event);
@ -1393,7 +1411,8 @@ async fn run_instance<P>(
true true
} }
}); });
#[cfg(wayland_platform)] let no_window_events = window_events.is_empty();
#[cfg(all(feature = "cctk", target_os = "linux"))]
window_events.push(core::Event::PlatformSpecific( window_events.push(core::Event::PlatformSpecific(
core::event::PlatformSpecific::Wayland( core::event::PlatformSpecific::Wayland(
core::event::wayland::Event::RequestResize, core::event::wayland::Event::RequestResize,
@ -1410,7 +1429,8 @@ async fn run_instance<P>(
&mut clipboard, &mut clipboard,
&mut messages, &mut messages,
); );
let mut needs_redraw = !messages.is_empty(); let mut needs_redraw =
!no_window_events || !messages.is_empty();
if let Some(requested_size) = if let Some(requested_size) =
clipboard.requested_logical_size.lock().unwrap().take() clipboard.requested_logical_size.lock().unwrap().take()
{ {
@ -1466,17 +1486,8 @@ async fn run_instance<P>(
mouse_interaction, mouse_interaction,
.. ..
} => { } => {
let mouse_changed =
window.mouse_interaction != mouse_interaction;
window.update_mouse(mouse_interaction); window.update_mouse(mouse_interaction);
if mouse_changed {
window.request_redraw(
core::window::RedrawRequest::NextFrame,
);
}
#[cfg(not(feature = "unconditional-rendering"))] #[cfg(not(feature = "unconditional-rendering"))]
window.request_redraw(_redraw_request); window.request_redraw(_redraw_request);
} }
@ -1536,7 +1547,22 @@ async fn run_instance<P>(
)); ));
for action in actions { for action in actions {
let exited = run_action!(action); let exited = run_action(
action,
&program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
);
if exited { if exited {
runtime.track(None.into_iter()); runtime.track(None.into_iter());
} }
@ -1746,6 +1772,7 @@ async fn run_instance<P>(
}; };
// search windows for widget with operation // search windows for widget with operation
if result.is_none() { if result.is_none() {
log::warn!( log::warn!(
"start_dnd: widget {:?} not found; drag will fail", "start_dnd: widget {:?} not found; drag will fail",
@ -2079,11 +2106,6 @@ fn run_action<'a, P, C>(
is_window_opening: &mut bool, is_window_opening: &mut bool,
system_theme: &mut theme::Mode, system_theme: &mut theme::Mode,
platform_specific: &mut crate::platform_specific::PlatformSpecific, platform_specific: &mut crate::platform_specific::PlatformSpecific,
dnd_surface: &mut Option<
Arc<Box<dyn HasWindowHandle + Send + Sync + 'static>>,
>,
dnd_surface_id: &mut Option<window::Id>,
is_daemon: bool,
) -> bool ) -> bool
where where
P: Program, P: Program,
@ -2137,7 +2159,7 @@ where
_ = control_sender.start_send(Control::Cleanup(id)).ok(); _ = control_sender.start_send(Control::Cleanup(id)).ok();
} }
let proxy = clipboard.proxy(); let proxy = clipboard.proxy();
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
platform_specific platform_specific
.send_wayland(platform_specific::Action::RemoveWindow(id)); .send_wayland(platform_specific::Action::RemoveWindow(id));
if let Some(window) = window_manager.remove(id) { if let Some(window) = window_manager.remove(id) {
@ -2168,7 +2190,7 @@ where
)); ));
} }
if window_manager.is_empty() && !is_daemon { if window_manager.is_empty() {
*compositor = None; *compositor = None;
} }
} }
@ -2598,10 +2620,6 @@ where
} }
iced_runtime::dnd::DndAction::EndDnd => { iced_runtime::dnd::DndAction::EndDnd => {
clipboard.end_dnd(); clipboard.end_dnd();
*dnd_surface = None;
if let Some(id) = dnd_surface_id.take() {
platform_specific.retain_subsurfaces(|other| other != id);
}
} }
iced_runtime::dnd::DndAction::PeekDnd(m, channel) => { iced_runtime::dnd::DndAction::PeekDnd(m, channel) => {
let data = clipboard.peek_dnd(m); let data = clipboard.peek_dnd(m);

View file

@ -1,20 +1,24 @@
//! Wayland specific shell //! Wayland specific shell
//! //!
use std::collections::HashMap; use std::{borrow::Cow, collections::HashMap, sync::Arc};
use iced_graphics::compositor; #[cfg(all(feature = "cctk", target_os = "linux"))]
use cctk::sctk::reexports::client::Connection;
use iced_graphics::{Compositor, compositor};
use iced_runtime::{ use iced_runtime::{
core::{Vector, window}, core::{Vector, window},
user_interface, platform_specific, user_interface,
}; };
use winit::raw_window_handle::HasWindowHandle; use winit::raw_window_handle::HasWindowHandle;
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
pub mod wayland; pub mod wayland;
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
pub use wayland::*; pub use wayland::*;
#[cfg(all(feature = "cctk", target_os = "linux"))]
use wayland_backend::client::Backend;
use crate::{CreateCompositor, Program, WindowManager}; use crate::{CreateCompositor, Program, WindowManager};
@ -31,7 +35,7 @@ pub type UserInterfaces<'a, P> = HashMap<
#[derive(Debug)] #[derive(Debug)]
pub enum Event { pub enum Event {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
Wayland(sctk_event::SctkEvent), Wayland(sctk_event::SctkEvent),
} }
@ -57,7 +61,7 @@ impl SurfaceIdWrapper {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct PlatformSpecific { pub struct PlatformSpecific {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
wayland: WaylandSpecific, wayland: WaylandSpecific,
} }
@ -67,7 +71,7 @@ impl PlatformSpecific {
action: iced_runtime::platform_specific::Action, action: iced_runtime::platform_specific::Action,
) { ) {
match action { match action {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
iced_runtime::platform_specific::Action::Wayland(a) => { iced_runtime::platform_specific::Action::Wayland(a) => {
self.send_wayland(wayland::Action::Action(a)); self.send_wayland(wayland::Action::Action(a));
} }
@ -78,14 +82,14 @@ impl PlatformSpecific {
&mut self, &mut self,
keep: F, keep: F,
) { ) {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
{ {
self.wayland.retain_subsurfaces(keep); self.wayland.retain_subsurfaces(keep);
} }
} }
pub(crate) fn clear_subsurface_list(&mut self) { pub(crate) fn clear_subsurface_list(&mut self) {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
{ {
self.wayland.clear_subsurface_list(); self.wayland.clear_subsurface_list();
} }
@ -96,7 +100,7 @@ impl PlatformSpecific {
id: window::Id, id: window::Id,
window: &dyn HasWindowHandle, window: &dyn HasWindowHandle,
) { ) {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
{ {
use cctk::sctk::reexports::client::{ use cctk::sctk::reexports::client::{
Proxy, protocol::wl_surface::WlSurface, Proxy, protocol::wl_surface::WlSurface,
@ -147,15 +151,12 @@ impl PlatformSpecific {
pub(crate) fn create_surface( pub(crate) fn create_surface(
&mut self, &mut self,
) -> Option<Box<dyn HasWindowHandle + Send + Sync + 'static>> { ) -> Option<Box<dyn HasWindowHandle + Send + Sync + 'static>> {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
{ {
self.wayland.create_surface() return self.wayland.create_surface();
} }
#[cfg(not(wayland_platform))]
{
None None
} }
}
pub(crate) fn update_surface_shm( pub(crate) fn update_surface_shm(
&mut self, &mut self,
@ -166,7 +167,7 @@ impl PlatformSpecific {
data: &[u8], data: &[u8],
offset: Vector, offset: Vector,
) { ) {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
{ {
return self.wayland.update_surface_shm( return self.wayland.update_surface_shm(
surface, width, height, scale, data, offset, surface, width, height, scale, data, offset,
@ -194,7 +195,7 @@ pub(crate) async fn handle_event<'a, 'b, P>(
P: Program, P: Program,
{ {
match e { match e {
#[cfg(wayland_platform)] #[cfg(all(feature = "cctk", target_os = "linux"))]
Event::Wayland(e) => { Event::Wayland(e) => {
platform_specific platform_specific
.wayland .wayland

View file

@ -1,8 +1,8 @@
use crate::core::window::Id as SurfaceId; use crate::core::window::Id as SurfaceId;
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{self, wayland}, platform_specific::{self, wayland},
task, task, Action, Task,
}; };
pub fn request_token( pub fn request_token(

View file

@ -1,17 +0,0 @@
use iced_futures::core::window;
use iced_runtime::{
self, Action, Task,
platform_specific::{self, wayland},
task,
};
pub fn blur(
id: window::Id,
blur: Option<Vec<iced_runtime::core::Rectangle>>,
) -> Task<()> {
task::oneshot(|_| {
Action::PlatformSpecific(platform_specific::Action::Wayland(
wayland::Action::BlurSurface(id, blur),
))
})
}

View file

@ -1,11 +1,11 @@
use iced_futures::core::window; use iced_futures::core::window;
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{ platform_specific::{
self, self,
wayland::{self, CornerRadius}, wayland::{self, CornerRadius},
}, },
task, task, Action, Task,
}; };
pub fn corner_radius( pub fn corner_radius(

View file

@ -1,7 +1,7 @@
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{self, wayland}, platform_specific::{self, wayland},
task, task, Action, Task,
}; };
pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> { pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> {

View file

@ -1,9 +1,8 @@
//! Interact with the window of your application. //! Interact with the window of your application.
use crate::core::Rectangle;
use crate::core::window::Id as SurfaceId; use crate::core::window::Id as SurfaceId;
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{ platform_specific::{
self, self,
wayland::{ wayland::{
@ -11,7 +10,7 @@ use iced_runtime::{
layer_surface::{IcedMargin, SctkLayerSurfaceSettings}, layer_surface::{IcedMargin, SctkLayerSurfaceSettings},
}, },
}, },
task, task, Action, Task,
}; };
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer}; pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
@ -115,26 +114,3 @@ pub fn set_layer<Message>(id: SurfaceId, layer: Layer) -> Task<Message> {
)), )),
)) ))
} }
pub fn set_padding<Message>(
id: SurfaceId,
padding: IcedMargin,
) -> Task<Message> {
task::effect(Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
wayland::layer_surface::Action::Padding { id, padding },
)),
))
}
/// <https://wayland.app/protocols/wayland#wl_surface:request:set_input_region>
pub fn set_input_zone<Message>(
id: SurfaceId,
zone: Option<Vec<Rectangle>>,
) -> Task<Message> {
task::effect(Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
wayland::layer_surface::Action::InputZone { id, zone },
)),
))
}

View file

@ -1,7 +1,6 @@
//! Interact with the wayland objects of your application. //! Interact with the wayland objects of your application.
pub mod activation; pub mod activation;
pub mod blur;
pub mod corner_radius; pub mod corner_radius;
pub mod keyboard_shortcuts_inhibit; pub mod keyboard_shortcuts_inhibit;
pub mod layer_surface; pub mod layer_surface;

View file

@ -1,8 +1,7 @@
use iced_futures::core::window::Id; use iced_futures::core::window::Id;
use iced_runtime::{ use iced_runtime::{
Action, Task,
platform_specific::{self, wayland}, platform_specific::{self, wayland},
task, task, Action, Task,
}; };
/// Request subscription for overlap notification events on the surface /// Request subscription for overlap notification events on the surface

View file

@ -1,15 +1,12 @@
//! Interact with the popups of your application. //! Interact with the popups of your application.
use crate::core::window::Id as SurfaceId; use crate::core::window::Id as SurfaceId;
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{ platform_specific::{
self, self,
wayland::{ wayland::{self, popup::SctkPopupSettings},
self,
popup::{SctkPopupSettings, SctkPositioner},
}, },
}, task, Action, Task,
task,
}; };
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:get_popup> /// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:get_popup>
@ -35,18 +32,6 @@ pub fn set_size<Message>(
)) ))
} }
/// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:reposition>
pub fn reposition<Message>(
id: SurfaceId,
positioner: SctkPositioner,
) -> Task<Message> {
task::effect(Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::Popup(
wayland::popup::Action::Reposition { id, positioner },
)),
))
}
/// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:destroy> /// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:destroy>
pub fn destroy_popup<Message>(id: SurfaceId) -> Task<Message> { pub fn destroy_popup<Message>(id: SurfaceId) -> Task<Message> {
task::effect(Action::PlatformSpecific( task::effect(Action::PlatformSpecific(

View file

@ -1,9 +1,9 @@
use crate::core::window::Id as SurfaceId; use crate::core::window::Id as SurfaceId;
use cctk::sctk::reexports::client::protocol::wl_output::WlOutput; use cctk::sctk::reexports::client::protocol::wl_output::WlOutput;
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{self, wayland}, platform_specific::{self, wayland},
task, task, Action, Task,
}; };
pub fn lock<Message>() -> Task<Message> { pub fn lock<Message>() -> Task<Message> {

View file

@ -1,12 +1,12 @@
use crate::core::window::Id as SurfaceId; use crate::core::window::Id as SurfaceId;
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer}; pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{ platform_specific::{
self, self,
wayland::{self, subsurface::SctkSubsurfaceSettings}, wayland::{self, subsurface::SctkSubsurfaceSettings},
}, },
task, task, Action, Task,
}; };
pub fn get_subsurface<Message>( pub fn get_subsurface<Message>(

View file

@ -1,5 +1,4 @@
pub mod control_flow; pub mod control_flow;
pub mod popup;
pub mod proxy; pub mod proxy;
pub mod state; pub mod state;
@ -7,7 +6,6 @@ pub mod state;
use crate::platform_specific::SurfaceIdWrapper; use crate::platform_specific::SurfaceIdWrapper;
use crate::{ use crate::{
Control, Control,
event_loop::popup::PopupManager,
futures::futures::channel::mpsc, futures::futures::channel::mpsc,
handlers::{ handlers::{
ext_background_effect, overlap::OverlapNotifyV1, ext_background_effect, overlap::OverlapNotifyV1,
@ -35,7 +33,7 @@ use cctk::{
reexports::{ reexports::{
calloop::{self, EventLoop}, calloop::{self, EventLoop},
client::{ client::{
Connection, Proxy, globals::registry_queue_init, ConnectError, Connection, Proxy, globals::registry_queue_init,
}, },
}, },
registry::RegistryState, registry::RegistryState,
@ -48,6 +46,8 @@ use cctk::{
}; };
use raw_window_handle::HasDisplayHandle; use raw_window_handle::HasDisplayHandle;
use state::{FrameStatus, SctkWindow, send_event}; use state::{FrameStatus, SctkWindow, send_event};
#[cfg(feature = "a11y")]
use std::sync::{Arc, Mutex};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fmt::Debug, fmt::Debug,
@ -71,8 +71,8 @@ pub struct SctkEventLoop {
pub(crate) state: SctkState, pub(crate) state: SctkState,
} }
#[derive(Debug)]
pub enum Error { pub enum Error {
Connect(ConnectError),
Calloop(calloop::Error), Calloop(calloop::Error),
Global(GlobalError), Global(GlobalError),
NoDisplayHandle, NoDisplayHandle,
@ -129,7 +129,7 @@ impl SctkEventLoop {
crate::platform_specific::Action::ResizeWindow(id) => { crate::platform_specific::Action::ResizeWindow(id) => {
if let Some((_, v)) = state.windows.iter() if let Some((_, v)) = state.windows.iter()
.find(|w| w.id == id) .find(|w| w.id == id)
.map(|w| state.corner_radii.get(&id)) .map(|w| w.corner_radius.as_ref())
.unwrap_or_default() { .unwrap_or_default() {
_ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, *v)); _ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, *v));
} }
@ -138,7 +138,7 @@ impl SctkEventLoop {
window, window,
id, id,
) => { ) => {
state.windows.push(SctkWindow { window, id }); state.windows.push(SctkWindow { window, id, corner_radius: Default::default() });
if let Some(v) = state.pending_corner_radius.remove(&id) { if let Some(v) = state.pending_corner_radius.remove(&id) {
_ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, Some(v))); _ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, Some(v)));
} }
@ -179,41 +179,13 @@ impl SctkEventLoop {
} }
} }
crate::platform_specific::Action::SetCursor( crate::platform_specific::Action::SetCursor(
surface,
icon, icon,
) => { ) => {
state.record_cursor(surface.clone(), |r| { if let Some(seat) = state.seats.get_mut(0) {
r.icon = Some(icon);
r.hidden = false;
});
if let Some(seat) = state
.seats
.get_mut(0)
.filter(|s| s.is_over(&surface))
{
seat.icon = Some(icon); seat.icon = Some(icon);
seat.hidden = false;
seat.set_cursor(&state.connection, icon); seat.set_cursor(&state.connection, icon);
} }
} }
crate::platform_specific::Action::SetCursorVisible(
surface,
visible,
) => {
state.record_cursor(surface.clone(), |r| {
r.hidden = !visible;
});
if let Some(seat) = state
.seats
.get_mut(0)
.filter(|s| s.is_over(&surface))
{
seat.set_cursor_visible(
&state.connection,
visible,
);
}
}
crate::platform_specific::Action::RequestRedraw( crate::platform_specific::Action::RequestRedraw(
id, id,
) => { ) => {
@ -378,7 +350,7 @@ impl SctkEventLoop {
), ),
corner_radius_manager: registry_state.bind_one::<CosmicCornerRadiusManagerV1, _, _>( corner_radius_manager: registry_state.bind_one::<CosmicCornerRadiusManagerV1, _, _>(
&qh, &qh,
1..=2, 1..=1,
(), (),
).ok(), ).ok(),
toplevel_manager: ToplevelManagerState::try_new( toplevel_manager: ToplevelManagerState::try_new(
@ -405,15 +377,13 @@ impl SctkEventLoop {
seats: Vec::new(), seats: Vec::new(),
windows: Vec::new(), windows: Vec::new(),
blur_surfaces: HashMap::new(), blur_surfaces: HashMap::new(),
corner_radii: HashMap::new(),
layer_surfaces: Vec::new(), layer_surfaces: Vec::new(),
popmgr: PopupManager::default(), popups: Vec::new(),
lock_surfaces: Vec::new(), lock_surfaces: Vec::new(),
subsurfaces: Vec::new(), subsurfaces: Vec::new(),
touch_points: HashMap::new(), touch_points: HashMap::new(),
sctk_events: Vec::new(), sctk_events: Vec::new(),
frame_status: HashMap::new(), frame_status: HashMap::new(),
cursor_requests: HashMap::new(),
fractional_scaling_manager, fractional_scaling_manager,
viewporter_state, viewporter_state,
compositor_updates: Default::default(), compositor_updates: Default::default(),
@ -432,7 +402,6 @@ impl SctkEventLoop {
preedit: None, preedit: None,
pending_delete: None, pending_delete: None,
pending_commit: None, pending_commit: None,
pending_blur: HashMap::new(),
}, },
_features: Default::default(), _features: Default::default(),
}; };
@ -519,23 +488,6 @@ impl SctkEventLoop {
let had_events = !state.state.sctk_events.is_empty(); let had_events = !state.state.sctk_events.is_empty();
let mut wake_up = had_events; let mut wake_up = had_events;
for e in state.state.sctk_events.drain(..) {
if let SctkEvent::Winit(id, e) = e {
_ = state
.state
.events_sender
.unbounded_send(Control::Winit(id, e));
} else {
_ =
state
.state
.events_sender
.unbounded_send(Control::PlatformSpecific(
crate::platform_specific::Event::Wayland(e),
));
}
}
for s in for s in
state state
.state .state
@ -545,8 +497,8 @@ impl SctkEventLoop {
.chain( .chain(
state state
.state .state
.popmgr .popups
.popups() .iter()
.map(|s| s.popup.wl_surface()), .map(|s| s.popup.wl_surface()),
) )
.chain( .chain(
@ -580,6 +532,22 @@ impl SctkEventLoop {
); );
} }
for e in state.state.sctk_events.drain(..) {
if let SctkEvent::Winit(id, e) = e {
_ = state
.state
.events_sender
.unbounded_send(Control::Winit(id, e));
} else {
_ =
state
.state
.events_sender
.unbounded_send(Control::PlatformSpecific(
crate::platform_specific::Event::Wayland(e),
));
}
}
if wake_up { if wake_up {
state.state.proxy.wake_up(); state.state.proxy.wake_up();
} }

View file

@ -1,189 +0,0 @@
use std::sync::Mutex;
use std::sync::Arc;
use cctk::sctk::shell::xdg::popup::PopupConfigure;
use cctk::sctk::shell::xdg::popup::Popup;
use iced_runtime::{platform_specific, core::window::Id};
use wayland_client::protocol::wl_surface::WlSurface;
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
use crate::event_loop::state::Common;
use crate::event_loop::state::SctkPopupData;
#[derive(Debug, Default)]
pub struct PopupManager {
chains: Vec<Vec<SctkPopup>>,
}
impl PopupManager {
pub(crate) fn popups(&self) -> impl Iterator<Item = &SctkPopup> {
self.chains.iter().map(|c| c.iter()).flatten()
}
pub(crate) fn chain_for_popup_mut(
&mut self,
id: &WlSurface,
) -> Option<(&mut Vec<SctkPopup>, usize)> {
let pos = self.chains.iter().enumerate().find_map(move |(pos, c)| {
if let Some(pop_pos) =
c.into_iter().position(|p| p.popup.wl_surface() == id)
{
Some((pos, pop_pos))
} else {
None
}
});
pos.map(|pos| (&mut self.chains[pos.0], pos.1))
}
pub(crate) fn chain_for_popup(
&self,
id: &WlSurface,
) -> Option<(&Vec<SctkPopup>, usize)> {
let pos = self.chains.iter().enumerate().find_map(move |(pos, c)| {
if let Some(pop_pos) =
c.into_iter().position(|p| p.popup.wl_surface() == id)
{
Some((pos, pop_pos))
} else {
None
}
});
pos.map(|pos| (&self.chains[pos.0], pos.1))
}
pub(crate) fn popup(&self, id: &WlSurface) -> Option<&SctkPopup> {
self.chains.iter().find_map(move |c| {
c.into_iter().find(|p| p.popup.wl_surface() == id)
})
}
pub(crate) fn popup_mut(
&mut self,
id: &WlSurface,
) -> Option<&mut SctkPopup> {
self.chains.iter_mut().find_map(move |c| {
c.into_iter().find(|p| p.popup.wl_surface() == id)
})
}
pub(crate) fn popup_id(&self, id: Id) -> Option<&SctkPopup> {
self.chains
.iter()
.find_map(move |c| c.into_iter().find(|p| p.data.id == id))
}
pub(crate) fn popup_id_mut(&mut self, id: Id) -> Option<&mut SctkPopup> {
self.chains
.iter_mut()
.find_map(move |c| c.into_iter().find(|p| p.data.id == id))
}
pub(crate) fn active_grab(&self) -> Option<&SctkPopup> {
self.chains
.iter()
.find_map(|c| c.into_iter().rev().find(|p| p.data.grab))
}
pub(crate) fn root_grab(&self) -> Option<&SctkPopup> {
self.chains
.iter()
.find_map(|c| c.into_iter().find(|p| p.data.grab))
}
pub(crate) fn push(&mut self, popup: SctkPopup) {
if let Some((chain, _pos)) =
self.chain_for_popup_mut(popup.data.parent.wl_surface())
{
// TODO should we return an error if a popup is attempted to be added to a non-leaf popup of a chain?
chain.push(popup);
} else {
if let Some(empty) = self.chains.iter_mut().find(|c| c.is_empty()) {
empty.push(popup);
} else {
self.chains.push(vec![popup]);
}
}
}
pub(crate) fn remove(
&mut self,
popup: &WlSurface,
) -> Option<impl Iterator<Item = SctkPopup>> {
// must perform cleanup so that the popups are dropped in the correct order
let ret = if let Some((chain, mut pos)) =
self.chain_for_popup_mut(popup)
{
// TODO should we return an error if a popup is attempted to be added to a non-leaf popup of a chain?
while let Some(p) = pos.checked_sub(1).and_then(|p| chain.get(p)) {
if p.close_with_children {
pos -= 1;
} else {
break;
}
}
Some(chain.drain(pos..).rev())
} else {
None
};
return ret;
}
pub(crate) fn remove_ignore_children(
&mut self,
popup: &WlSurface,
) -> Option<impl Iterator<Item = SctkPopup>> {
// must perform cleanup so that the popups are dropped in the correct order
let ret = if let Some((chain, pos)) = self.chain_for_popup_mut(popup) {
Some(chain.drain(pos..).rev())
} else {
None
};
return ret;
}
}
#[derive(Debug)]
pub struct SctkPopup {
pub(crate) popup: Popup,
pub(crate) last_configure: Option<PopupConfigure>,
pub(crate) _pending_requests:
Vec<platform_specific::wayland::popup::Action>,
pub(crate) data: SctkPopupData,
pub(crate) common: Arc<Mutex<Common>>,
pub(crate) wp_fractional_scale: Option<WpFractionalScaleV1>,
pub(crate) close_with_children: bool,
}
impl SctkPopup {
pub(crate) fn set_size(&mut self, w: u32, h: u32, token: u32) {
let guard = self.common.lock().unwrap();
if guard.size.width == w && guard.size.height == h {
return;
}
drop(guard);
// update geometry
self.popup
.xdg_surface()
.set_window_geometry(0, 0, w as i32, h as i32);
self.update_viewport(w, h);
// update positioner
self.data.positioner.set_size(w as i32, h as i32);
self.popup.reposition(&self.data.positioner, token);
}
pub(crate) fn update_viewport(&mut self, w: u32, h: u32) {
let common = self.common.lock().unwrap();
if common.size.width == w && common.size.height == h {
return;
}
if let Some(viewport) = common.wp_viewport.as_ref() {
// Set inner size without the borders.
viewport.set_destination(w as i32, h as i32);
}
}
}

View file

@ -1,8 +1,8 @@
use cctk::sctk::reexports::calloop; use cctk::sctk::reexports::calloop;
use iced_futures::futures::{ use iced_futures::futures::{
Sink,
channel::mpsc, channel::mpsc,
task::{Context, Poll}, task::{Context, Poll},
Sink,
}; };
use std::pin::Pin; use std::pin::Pin;

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use cctk::sctk; use cctk::sctk;
use iced_futures::futures::SinkExt;
use iced_runtime::core::Rectangle; use iced_runtime::core::Rectangle;
use iced_runtime::platform_specific::wayland::Action; use iced_runtime::platform_specific::wayland::Action;
use sctk::globals::GlobalData; use sctk::globals::GlobalData;
@ -12,7 +11,7 @@ use wayland_protocols::ext::background_effect::v1::client::ext_background_effect
use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1; use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1;
use crate::event_loop::state::SctkState; use crate::event_loop::state::SctkState;
use crate::{platform_specific, window}; use crate::window;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ExtBackgroundEffectManager { pub struct ExtBackgroundEffectManager {
@ -71,15 +70,8 @@ impl Dispatch<ExtBackgroundEffectManagerV1, GlobalData, SctkState>
state.ext_background_effect_manager.as_mut() state.ext_background_effect_manager.as_mut()
{ {
bg_effect_mgr.capabilities = capability; bg_effect_mgr.capabilities = capability;
if capability.contains(Capability::Blur) { queued_actions =
queued_actions = bg_effect_mgr bg_effect_mgr.queued_blur_actions.drain().collect();
.queued_blur_actions
.drain()
.collect();
state
.sctk_events
.push(crate::sctk_event::SctkEvent::Blur);
}
} }
for (id, rects) in queued_actions { for (id, rects) in queued_actions {
_ = state.handle_action(Action::BlurSurface(id, rects)); _ = state.handle_action(Action::BlurSurface(id, rects));

View file

@ -4,46 +4,10 @@ use crate::platform_specific::wayland::{
}; };
use cctk::sctk::{ use cctk::sctk::{
delegate_keyboard, delegate_keyboard,
seat::keyboard::{KeyboardHandler, Keysym, Modifiers}, seat::keyboard::{KeyboardHandler, Keysym},
}; };
use cctk::sctk::{reexports::client::Proxy, seat::keyboard::RawModifiers}; use cctk::sctk::{reexports::client::Proxy, seat::keyboard::RawModifiers};
fn modifiers_from_keysyms(
keysyms: &[Keysym],
previous: Modifiers,
) -> Modifiers {
let mut modifiers = Modifiers {
caps_lock: previous.caps_lock,
num_lock: previous.num_lock,
..Modifiers::default()
};
for keysym in keysyms {
match keysym.raw() {
xkeysym::key::Shift_L | xkeysym::key::Shift_R => {
modifiers.shift = true
}
xkeysym::key::Control_L | xkeysym::key::Control_R => {
modifiers.ctrl = true
}
xkeysym::key::Alt_L | xkeysym::key::Alt_R => modifiers.alt = true,
xkeysym::key::Super_L
| xkeysym::key::Super_R
| xkeysym::key::Hyper_L
| xkeysym::key::Hyper_R
| xkeysym::key::Meta_L
| xkeysym::key::Meta_R => modifiers.logo = true,
xkeysym::key::Caps_Lock | xkeysym::key::Shift_Lock => {
modifiers.caps_lock = true
}
xkeysym::key::Num_Lock => modifiers.num_lock = true,
_ => {}
}
}
modifiers
}
impl KeyboardHandler for SctkState { impl KeyboardHandler for SctkState {
fn enter( fn enter(
&mut self, &mut self,
@ -53,9 +17,9 @@ impl KeyboardHandler for SctkState {
surface: &cctk::sctk::reexports::client::protocol::wl_surface::WlSurface, surface: &cctk::sctk::reexports::client::protocol::wl_surface::WlSurface,
_serial: u32, _serial: u32,
_raw: &[u32], _raw: &[u32],
keysyms: &[Keysym], _keysyms: &[Keysym],
) { ) {
let (i, mut is_active, seat, modifiers) = { let (i, mut is_active, seat) = {
let (i, is_active, my_seat) = let (i, is_active, my_seat) =
match self.seats.iter_mut().enumerate().find_map(|(i, s)| { match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
if s.kbd.as_ref() == Some(keyboard) { if s.kbd.as_ref() == Some(keyboard) {
@ -77,11 +41,9 @@ impl KeyboardHandler for SctkState {
surface surface
}; };
_ = my_seat.kbd_focus.replace(surface.clone()); _ = my_seat.kbd_focus.replace(surface.clone());
let modifiers = modifiers_from_keysyms(keysyms, my_seat._modifiers);
my_seat._modifiers = modifiers;
let seat = my_seat.seat.clone(); let seat = my_seat.seat.clone();
(i, is_active, seat, modifiers) (i, is_active, seat)
}; };
if !is_active && self.seats[0].kbd_focus.is_none() { if !is_active && self.seats[0].kbd_focus.is_none() {
@ -111,12 +73,6 @@ impl KeyboardHandler for SctkState {
seat_id: seat.clone(), seat_id: seat.clone(),
surface: surface.clone(), surface: surface.clone(),
}); });
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Modifiers(modifiers),
kbd_id: keyboard.clone(),
seat_id: seat.clone(),
surface: surface.clone(),
});
} }
} }
} }
@ -170,7 +126,6 @@ impl KeyboardHandler for SctkState {
if self.windows.iter().any(|w| w.window.id() == id) { if self.windows.iter().any(|w| w.window.id() == id) {
continue; continue;
} }
let modifiers = s._modifiers;
self.sctk_events.push(SctkEvent::Winit( self.sctk_events.push(SctkEvent::Winit(
id, id,
winit::event::WindowEvent::Focused(true), winit::event::WindowEvent::Focused(true),
@ -182,12 +137,6 @@ impl KeyboardHandler for SctkState {
kbd_id: s.kbd.clone().unwrap(), kbd_id: s.kbd.clone().unwrap(),
seat_id: s.seat.clone(), seat_id: s.seat.clone(),
surface: surface.clone(), surface: surface.clone(),
});
self.sctk_events.push(SctkEvent::KeyboardEvent {
variant: KeyboardEventVariant::Modifiers(modifiers),
kbd_id: s.kbd.clone().unwrap(),
seat_id: s.seat.clone(),
surface: surface.clone(),
}) })
} }
} }
@ -299,7 +248,6 @@ impl KeyboardHandler for SctkState {
}; };
let seat_id = my_seat.seat.clone(); let seat_id = my_seat.seat.clone();
let kbd_id = keyboard.clone(); let kbd_id = keyboard.clone();
my_seat._modifiers = modifiers;
if is_active { if is_active {
if let Some(surface) = my_seat.kbd_focus.clone() { if let Some(surface) = my_seat.kbd_focus.clone() {

View file

@ -1,5 +1,8 @@
use crate::platform_specific::wayland::{ use crate::{
event_loop::state::FrameStatus,
platform_specific::wayland::{
event_loop::state::SctkState, sctk_event::SctkEvent, event_loop::state::SctkState, sctk_event::SctkEvent,
},
}; };
use cctk::sctk::{ use cctk::sctk::{
delegate_pointer, delegate_pointer,
@ -31,7 +34,7 @@ impl PointerHandler for SctkState {
// track events, but only forward for the active seat // track events, but only forward for the active seat
for e in events { for e in events {
if !my_seat.hidden && my_seat.active_icon != my_seat.icon { if my_seat.active_icon != my_seat.icon {
// Restore cursor that was set by appliction, or default // Restore cursor that was set by appliction, or default
my_seat.set_cursor( my_seat.set_cursor(
conn, conn,
@ -47,6 +50,14 @@ impl PointerHandler for SctkState {
continue; continue;
} }
let entry = self
.frame_status
.entry(e.surface.id())
.or_insert(FrameStatus::RequestedRedraw);
if matches!(entry, FrameStatus::Received) {
*entry = FrameStatus::Ready;
}
self.sctk_events.push(SctkEvent::PointerEvent { self.sctk_events.push(SctkEvent::PointerEvent {
variant: PointerEvent { variant: PointerEvent {
surface: e.surface.clone(), surface: e.surface.clone(),
@ -60,19 +71,10 @@ impl PointerHandler for SctkState {
match e.kind { match e.kind {
PointerEventKind::Enter { .. } => { PointerEventKind::Enter { .. } => {
_ = my_seat.ptr_focus.replace(e.surface.clone()); _ = my_seat.ptr_focus.replace(e.surface.clone());
if let Some(want) =
self.cursor_requests.get(&e.surface.id())
{
my_seat.icon = want.icon;
my_seat.set_cursor_visible(conn, !want.hidden);
}
} }
PointerEventKind::Leave { .. } => { PointerEventKind::Leave { .. } => {
_ = my_seat.ptr_focus.take(); _ = my_seat.ptr_focus.take();
_ = my_seat.active_icon = None; _ = my_seat.active_icon = None;
// The next surface the pointer lands on decides what it
// looks like there
my_seat.hidden = false;
} }
PointerEventKind::Press { PointerEventKind::Press {
time, time,

View file

@ -4,9 +4,10 @@ use crate::platform_specific::wayland::{
}; };
use cctk::sctk::{ use cctk::sctk::{
delegate_seat, delegate_seat,
reexports::client::{Proxy, protocol::wl_keyboard::WlKeyboard}, reexports::client::{protocol::wl_keyboard::WlKeyboard, Proxy},
seat::{SeatHandler, keyboard::Modifiers, pointer::ThemeSpec}, seat::{pointer::ThemeSpec, SeatHandler},
}; };
use iced_runtime::keyboard::Modifiers;
use std::sync::Arc; use std::sync::Arc;
impl SeatHandler for SctkState { impl SeatHandler for SctkState {
@ -38,7 +39,6 @@ impl SeatHandler for SctkState {
last_touch_down: None, last_touch_down: None,
icon: None, icon: None,
active_icon: None, active_icon: None,
hidden: false,
}); });
} }
@ -66,7 +66,6 @@ impl SeatHandler for SctkState {
last_touch_down: None, last_touch_down: None,
icon: None, icon: None,
active_icon: None, active_icon: None,
hidden: false,
}); });
self.seats.last_mut().unwrap() self.seats.last_mut().unwrap()
} }

Some files were not shown because too many files have changed in this diff Show more