favorites: implement a favorite list

The right panel can now store a favorite list which is
saved into a settings file in the user config directory.
This commit is contained in:
Stéphane Cerveau 2021-12-21 17:37:17 +01:00
parent 41b5beee26
commit d07b72df4f
7 changed files with 625 additions and 43 deletions

390
Cargo.lock generated
View file

@ -2,6 +2,21 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.44" version = "1.0.44"
@ -14,19 +29,56 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
dependencies = [
"byteorder",
"safemem",
]
[[package]]
name = "bitflags"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "cairo-rs" name = "cairo-rs"
version = "0.14.7" version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9164355c892b026d6257e696dde5f3cb39beb3718297f0f161b562fe2ee3ab86" checksum = "9164355c892b026d6257e696dde5f3cb39beb3718297f0f161b562fe2ee3ab86"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"cairo-sys-rs", "cairo-sys-rs",
"glib", "glib",
"libc", "libc",
@ -44,6 +96,12 @@ dependencies = [
"system-deps 3.2.0", "system-deps 3.2.0",
] ]
[[package]]
name = "cc"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]] [[package]]
name = "cfg-expr" name = "cfg-expr"
version = "0.8.1" version = "0.8.1"
@ -68,12 +126,49 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "dtoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]] [[package]]
name = "either" name = "either"
version = "1.6.1" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "error-chain"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8"
dependencies = [
"backtrace",
]
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]] [[package]]
name = "field-offset" name = "field-offset"
version = "0.3.4" version = "0.3.4"
@ -167,7 +262,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f97a162c17214d1bf981af3f683156a0b1667dd1927057c4f0a68513251ecf0f" checksum = "f97a162c17214d1bf981af3f683156a0b1667dd1927057c4f0a68513251ecf0f"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"cairo-rs", "cairo-rs",
"gdk-pixbuf", "gdk-pixbuf",
"gdk4-sys", "gdk4-sys",
@ -194,13 +289,19 @@ dependencies = [
"system-deps 5.0.0", "system-deps 5.0.0",
] ]
[[package]]
name = "gimli"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]] [[package]]
name = "gio" name = "gio"
version = "0.14.8" version = "0.14.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711c3632b3ebd095578a9c091418d10fed492da9443f58ebc8f45efbeb215cb0" checksum = "711c3632b3ebd095578a9c091418d10fed492da9443f58ebc8f45efbeb215cb0"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-io", "futures-io",
@ -230,7 +331,7 @@ version = "0.14.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c515f1e62bf151ef6635f528d05b02c11506de986e43b34a5c920ef0b3796a4" checksum = "7c515f1e62bf151ef6635f528d05b02c11506de986e43b34a5c920ef0b3796a4"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-executor", "futures-executor",
@ -308,7 +409,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eff59ca46c4fc5087fd7a0c3770a71ea4b6e94f8c24c12e2c2e8538f9f6fd764" checksum = "eff59ca46c4fc5087fd7a0c3770a71ea4b6e94f8c24c12e2c2e8538f9f6fd764"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"cairo-rs", "cairo-rs",
"gdk4", "gdk4",
"glib", "glib",
@ -342,10 +443,12 @@ dependencies = [
"glib", "glib",
"gstreamer", "gstreamer",
"gtk4", "gtk4",
"log", "log 0.4.14",
"once_cell", "once_cell",
"serde",
"serde_any",
"x11", "x11",
"xml-rs", "xml-rs 0.8.4",
] ]
[[package]] [[package]]
@ -354,7 +457,7 @@ version = "0.17.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6a255f142048ba2c4a4dce39106db1965abe355d23f4b5335edea43a553faa4" checksum = "c6a255f142048ba2c4a4dce39106db1965abe355d23f4b5335edea43a553faa4"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"cfg-if", "cfg-if",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -389,7 +492,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58a04f421d1485ba4739e723199f5828bca05ab4e622ed39a96a342b6b1a6a3d" checksum = "58a04f421d1485ba4739e723199f5828bca05ab4e622ed39a96a342b6b1a6a3d"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"cairo-rs", "cairo-rs",
"field-offset", "field-offset",
"futures-channel", "futures-channel",
@ -450,6 +553,17 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "idna"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.1" version = "0.10.1"
@ -459,12 +573,39 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.103" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "log"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
dependencies = [
"log 0.4.14",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.14" version = "0.4.14"
@ -474,6 +615,18 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.6.4" version = "0.6.4"
@ -483,6 +636,16 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]] [[package]]
name = "muldiv" name = "muldiv"
version = "1.0.0" version = "1.0.0"
@ -519,6 +682,15 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "object"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.8.0" version = "1.8.0"
@ -531,7 +703,7 @@ version = "0.14.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "546fd59801e5ca735af82839007edd226fe7d3bb06433ec48072be4439c28581" checksum = "546fd59801e5ca735af82839007edd226fe7d3bb06433ec48072be4439c28581"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"glib", "glib",
"libc", "libc",
"once_cell", "once_cell",
@ -556,6 +728,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
[[package]]
name = "percent-encoding"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
[[package]] [[package]]
name = "pest" name = "pest"
version = "2.1.3" version = "2.1.3"
@ -596,7 +774,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
dependencies = [ dependencies = [
"thiserror", "thiserror",
"toml", "toml 0.5.8",
] ]
[[package]] [[package]]
@ -641,6 +819,23 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "ron"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9fa11b7a38511d46ff1959ae46ebb60bd8a746f17bdd0206b4c8de7559ac47b"
dependencies = [
"base64",
"bitflags 1.3.2",
"serde",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.3.3" version = "0.3.3"
@ -650,6 +845,18 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "safemem"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]] [[package]]
name = "semver" name = "semver"
version = "0.11.0" version = "0.11.0"
@ -673,6 +880,83 @@ name = "serde"
version = "1.0.130" version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-xml-any"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e281928a3e3104e809dbd19b78cef7ef7c29662cf2583a94c032485aa2e7586b"
dependencies = [
"error-chain",
"log 0.3.9",
"serde",
"xml-rs 0.6.1",
]
[[package]]
name = "serde_any"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38cb506febacc2cf6533279947bd37b69ce91782af1aedf31c7e6181a77d46ee"
dependencies = [
"failure",
"ron",
"serde",
"serde-xml-any",
"serde_json",
"serde_urlencoded",
"serde_yaml",
"toml 0.4.10",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
dependencies = [
"itoa 1.0.1",
"ryu",
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a"
dependencies = [
"dtoa",
"itoa 0.4.8",
"serde",
"url",
]
[[package]]
name = "serde_yaml"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef8099d3df28273c99a1728190c7a9f19d444c941044f64adf986bee7ec53051"
dependencies = [
"dtoa",
"linked-hash-map",
"serde",
"yaml-rust",
]
[[package]] [[package]]
name = "slab" name = "slab"
@ -715,6 +999,18 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "3.2.0" version = "3.2.0"
@ -729,7 +1025,7 @@ dependencies = [
"strum", "strum",
"strum_macros", "strum_macros",
"thiserror", "thiserror",
"toml", "toml 0.5.8",
"version-compare", "version-compare",
] ]
@ -742,7 +1038,7 @@ dependencies = [
"cfg-expr 0.9.0", "cfg-expr 0.9.0",
"heck", "heck",
"pkg-config", "pkg-config",
"toml", "toml 0.5.8",
"version-compare", "version-compare",
] ]
@ -766,6 +1062,30 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tinyvec"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "toml"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.5.8" version = "0.5.8"
@ -781,6 +1101,21 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-bidi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-normalization"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
dependencies = [
"tinyvec",
]
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.8.0" version = "1.8.0"
@ -793,6 +1128,17 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "url"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
dependencies = [
"idna",
"matches",
"percent-encoding",
]
[[package]] [[package]]
name = "version-compare" name = "version-compare"
version = "0.0.11" version = "0.0.11"
@ -837,8 +1183,26 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "xml-rs"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1945e12e16b951721d7976520b0832496ef79c31602c7a29d950de79ba74621"
dependencies = [
"bitflags 0.9.1",
]
[[package]] [[package]]
name = "xml-rs" name = "xml-rs"
version = "0.8.4" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View file

@ -14,3 +14,5 @@ log = "0.4.11"
once_cell = "1.7.2" once_cell = "1.7.2"
xml-rs = "0.8.4" xml-rs = "0.8.4"
x11 = { version = "2.18", features = ["xlib"] } x11 = { version = "2.18", features = ["xlib"] }
serde = "1.0"
serde_any = "0.5"

View file

@ -19,11 +19,12 @@
use glib::Value; use glib::Value;
use gtk::gdk::Rectangle; use gtk::gdk::Rectangle;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{gio, glib, graphene};
use gtk::{ use gtk::{
AboutDialog, Application, ApplicationWindow, Builder, Button, FileChooserAction, gdk::BUTTON_SECONDARY, AboutDialog, Application, ApplicationWindow, Builder, Button,
FileChooserDialog, PopoverMenu, ResponseType, Statusbar, Viewport, CellRendererText, FileChooserAction, FileChooserDialog, ListStore, PopoverMenu, ResponseType,
Statusbar, TreeView, TreeViewColumn, Viewport,
}; };
use gtk::{gio, glib, graphene};
use once_cell::unsync::OnceCell; use once_cell::unsync::OnceCell;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
@ -32,6 +33,7 @@ use std::{error, ops};
use crate::pipeline::{Pipeline, PipelineState}; use crate::pipeline::{Pipeline, PipelineState};
use crate::plugindialogs; use crate::plugindialogs;
use crate::settings::Settings;
use crate::graphmanager::{GraphView, Node, PortDirection}; use crate::graphmanager::{GraphView, Node, PortDirection};
@ -188,6 +190,109 @@ impl GPSApp {
dialog.show(); dialog.show();
} }
fn reset_favorite_list(&self, favorite_list: &TreeView) {
let model = ListStore::new(&[String::static_type()]);
favorite_list.set_model(Some(&model));
let favorites = Settings::get_favorites_list();
for favorite in favorites {
model.insert_with_values(None, &[(0, &favorite)]);
}
}
fn setup_favorite_list(&self) {
let favorite_list: TreeView = self
.builder
.object("favorites_list")
.expect("Couldn't get window");
let column = TreeViewColumn::new();
let cell = CellRendererText::new();
column.pack_start(&cell, true);
// Association of the view's column with the model's `id` column.
column.add_attribute(&cell, "text", 0);
column.set_title("favorites");
favorite_list.append_column(&column);
self.reset_favorite_list(&favorite_list);
let app_weak = self.downgrade();
favorite_list.connect_row_activated(move |tree_view, _tree_path, _tree_column| {
let app = upgrade_weak!(app_weak);
let selection = tree_view.selection();
if let Some((model, iter)) = selection.selected() {
let element_name = model
.get(&iter, 0)
.get::<String>()
.expect("Treeview selection, column 1");
println!("{}", element_name);
app.add_new_element(&element_name);
}
});
let gesture = gtk::GestureClick::new();
gesture.set_button(0);
let app_weak = self.downgrade();
gesture.connect_pressed(
glib::clone!(@weak favorite_list => move |gesture, _n_press, x, y| {
let app = upgrade_weak!(app_weak);
if gesture.current_button() == BUTTON_SECONDARY {
let selection = favorite_list.selection();
if let Some((model, iter)) = selection.selected() {
let element_name = model
.get(&iter, 0)
.get::<String>()
.expect("Treeview selection, column 1");
println!("{}", element_name);
let point = graphene::Point::new(x as f32,y as f32);
let pop_menu: PopoverMenu = app
.builder
.object("fav_pop_menu")
.expect("Couldn't get menu model for favorites");
pop_menu.set_pointing_to(&Rectangle {
x: point.to_vec2().x() as i32,
y: point.to_vec2().y() as i32,
width: 0,
height: 0,
});
// add an action to delete link
let action = gio::SimpleAction::new("favorite.remove", None);
let app_weak = app.downgrade();
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
let app = upgrade_weak!(app_weak);
Settings::remove_favorite(&element_name);
app.reset_favorite_list(&favorite_list);
pop_menu.unparent();
}));
if let Some(application) = app.window.application() {
application.add_action(&action);
}
pop_menu.show();
}
}
}),
);
favorite_list.add_controller(&gesture);
}
fn add_to_favorite_list(&self, element_name: String) {
let favorites = Settings::get_favorites_list();
if !favorites.contains(&element_name) {
let favorite_list: TreeView = self
.builder
.object("favorites_list")
.expect("Couldn't get window");
if let Some(model) = favorite_list.model() {
let list_store = model
.dynamic_cast::<ListStore>()
.expect("Could not cast to ListStore");
list_store.insert_with_values(None, &[(0, &element_name)]);
Settings::add_favorite(&element_name);
}
}
}
pub fn build_ui(&self, application: &Application) { pub fn build_ui(&self, application: &Application) {
let drawing_area_window: Viewport = self let drawing_area_window: Viewport = self
.builder .builder
@ -430,6 +535,17 @@ impl GPSApp {
width: 0, width: 0,
height: 0, height: 0,
}); });
let action = gio::SimpleAction::new("node.add-to-favorite", None);
let app_weak = app.downgrade();
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
let app = upgrade_weak!(app_weak);
println!("node.delete {}", node_id);
let node = app.graphview.borrow().node(&node_id).unwrap();
app.add_to_favorite_list(node.name());
pop_menu.unparent();
}));
application.add_action(&action);
let action = gio::SimpleAction::new("node.delete", None); let action = gio::SimpleAction::new("node.delete", None);
let app_weak = app.downgrade(); let app_weak = app.downgrade();
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| { action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
@ -479,6 +595,9 @@ impl GPSApp {
}), }),
) )
.expect("Failed to register node-right-clicked signal of graphview"); .expect("Failed to register node-right-clicked signal of graphview");
// Setup the favorite list
self.setup_favorite_list();
} }
// Downgrade to a weak reference // Downgrade to a weak reference
@ -489,11 +608,11 @@ impl GPSApp {
// Called when the application shuts down. We drop our app struct here // Called when the application shuts down. We drop our app struct here
fn drop(self) {} fn drop(self) {}
pub fn add_new_element(&self, element_name: String) { pub fn add_new_element(&self, element_name: &str) {
let graph_view = self.graphview.borrow_mut(); let graph_view = self.graphview.borrow_mut();
let node_id = graph_view.next_node_id(); let node_id = graph_view.next_node_id();
let pads = Pipeline::pads(&element_name, false); let pads = Pipeline::pads(element_name, false);
if Pipeline::element_is_uri_src_handler(&element_name) { if Pipeline::element_is_uri_src_handler(element_name) {
GPSApp::get_file_from_dialog(self, false, move |app, filename| { GPSApp::get_file_from_dialog(self, false, move |app, filename| {
println!("Open file {}", filename); println!("Open file {}", filename);
let node = app.graphview.borrow().node(&node_id).unwrap(); let node = app.graphview.borrow().node(&node_id).unwrap();
@ -504,11 +623,7 @@ impl GPSApp {
} }
graph_view.add_node_with_port( graph_view.add_node_with_port(
node_id, node_id,
Node::new( Node::new(node_id, element_name, Pipeline::element_type(element_name)),
node_id,
&element_name,
Pipeline::element_type(&element_name),
),
pads.0, pads.0,
pads.1, pads.1,
); );

View file

@ -36,6 +36,14 @@
</item> </item>
</section> </section>
</menu> </menu>
<menu id="fav_menu">
<section>
<item>
<attribute name="label" translatable="yes" comments="favorite menu entry delete the favorite">_Remove favorite</attribute>
<attribute name="action">app.favorite.remove</attribute>
</item>
</section>
</menu>
<menu id="node_menu"> <menu id="node_menu">
<section> <section>
<item> <item>
@ -60,6 +68,11 @@
<attribute name="action">app.node.properties</attribute> <attribute name="action">app.node.properties</attribute>
<attribute name="accel">&lt;primary&gt;n</attribute> <attribute name="accel">&lt;primary&gt;n</attribute>
</item> </item>
<item>
<attribute name="label" translatable="yes" comments="Node menu entry add to favorite">_Add to favorite</attribute>
<attribute name="action">app.node.add-to-favorite</attribute>
<attribute name="accel">&lt;primary&gt;A</attribute>
</item>
</section> </section>
</menu> </menu>
@ -68,7 +81,11 @@
<property name="authors">Stéphane Cerveau</property> <property name="authors">Stéphane Cerveau</property>
<property name="hide-on-close">True</property> <property name="hide-on-close">True</property>
</object> </object>
<object class="GtkAdjustment" id="scale_adjustment">
<property name="upper">100</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
<object class="GtkDialog" id="dialog-plugin-list"> <object class="GtkDialog" id="dialog-plugin-list">
<property name="transient-for">mainwindow</property> <property name="transient-for">mainwindow</property>
<property name="default-width">320</property> <property name="default-width">320</property>
@ -195,12 +212,14 @@
</child> </child>
</object> </object>
</child> </child>
<child> </object>
<object class="GtkScale"> </child>
<property name="draw-value">1</property> <child>
<property name="round-digits">1</property> <object class="GtkScale" id="scale-position">
</object> <property name="visible">True</property>
</child> <property name="can-focus">True</property>
<property name="adjustment">scale_adjustment</property>
<property name="round-digits">1</property>
</object> </object>
</child> </child>
<child> <child>
@ -227,12 +246,12 @@
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="vexpand">True</property> <property name="vexpand">True</property>
<property name="child"> <property name="child">
<object class="GtkTreeView"> <object class="GtkTreeView" id="favorites_list">
<child> <child>
<object class="GtkTreeViewColumn"> <object class="GtkPopoverMenu" id="fav_pop_menu">
<property name="title" translatable="yes">column</property> <property name="menu-model">fav_menu</property>
</object> </object>
</child> </child>
</object> </object>
</property> </property>
</object> </object>

View file

@ -24,6 +24,7 @@ mod common;
mod graphmanager; mod graphmanager;
mod pipeline; mod pipeline;
mod plugindialogs; mod plugindialogs;
mod settings;
use gtk::prelude::*; use gtk::prelude::*;
use crate::app::GPSApp; use crate::app::GPSApp;

View file

@ -106,16 +106,11 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) {
let app = upgrade_weak!(app_weak); let app = upgrade_weak!(app_weak);
let selection = tree_view.selection(); let selection = tree_view.selection();
if let Some((model, iter)) = selection.selected() { if let Some((model, iter)) = selection.selected() {
// Now getting back the values from the row corresponding to the
// iterator `iter`.
//
let element_name = model let element_name = model
.get(&iter, 1) .get(&iter, 1)
.get::<String>() .get::<String>()
.expect("Treeview selection, column 1"); .expect("Treeview selection, column 1");
println!("{}", element_name); app.add_new_element(&element_name);
app.add_new_element(element_name);
} }
}), }),
); );

86
src/settings.rs Normal file
View file

@ -0,0 +1,86 @@
use std::fs::create_dir_all;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::common;
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Settings {
pub favorites: Vec<String>,
}
impl Settings {
fn settings_file_exist() {
let s = Settings::get_settings_file_path();
if !s.exists() {
if let Some(parent_dir) = s.parent() {
if !parent_dir.exists() {
if let Err(e) = create_dir_all(parent_dir) {
println!(
"Error while trying to build settings snapshot_directory '{}': {}",
parent_dir.display(),
e
);
}
}
}
}
}
fn get_settings_file_path() -> PathBuf {
let mut path = glib::user_config_dir();
path.push(common::APPLICATION_NAME);
path.push("settings.toml");
path
}
// Public methods
pub fn add_favorite(favorite: &str) {
let mut settings = Settings::load_settings();
settings.favorites.sort();
settings.favorites.push(String::from(favorite));
Settings::save_settings(&settings);
}
pub fn remove_favorite(favorite: &str) {
let mut settings = Settings::load_settings();
settings.favorites.retain(|x| x != favorite);
Settings::save_settings(&settings);
}
pub fn get_favorites_list() -> Vec<String> {
let mut favorites = Vec::new();
let settings = Settings::load_settings();
for fav in settings.favorites {
favorites.push(fav);
}
favorites
}
// Save the provided settings to the settings path
pub fn save_settings(settings: &Settings) {
Settings::settings_file_exist();
let s = Settings::get_settings_file_path();
if let Err(e) = serde_any::to_file(&s, settings) {
println!("Error while trying to save file: {} {}", s.display(), e);
}
}
// Load the current settings
pub fn load_settings() -> Settings {
let s = Settings::get_settings_file_path();
if s.exists() && s.is_file() {
match serde_any::from_file::<Settings, _>(&s) {
Ok(s) => s,
Err(e) => {
println!("Error while opening '{}': {}", s.display(), e);
Settings::default()
}
}
} else {
Settings::default()
}
}
}