| author | |
| committer | |
| log | f3b369e572b4a689a2e3c0337a0ea4be5da31fd6 |
| tree | d0d0009e5599beb778a54a6f4aab19cc7a37cbff |
| parent | 1a9bd5592f8f9bd6781eb91b4b2c383af90a4f9b |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU |
The binder is three layers, its back and tabs, the cover currentColor paints and what
lies on the cover, which iOS's asset catalog links to and composes with the cover as a
template. The desktop shades the colour as it shades a section's, for each appearance.
Assisted-by: claude-opus-5.520 files changed, 138 insertions(+), 36 deletions(-)
apps/ios/Snowbound/Assets.xcassets/Notebook.imageset/Contents.json created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | { | |
| 2 | "images" : [ | |
| 3 | { | |
| 4 | "filename" : "notebook.svg", | |
| 5 | "idiom" : "universal" | |
| 6 | } | |
| 7 | ], | |
| 8 | "info" : { | |
| 9 | "author" : "xcode", | |
| 10 | "version" : 1 | |
| 11 | }, | |
| 12 | "properties" : { | |
| 13 | "preserves-vector-representation" : true, | |
| 14 | "template-rendering-intent" : "original" | |
| 15 | } | |
| 16 | } |
apps/ios/Snowbound/Assets.xcassets/Notebook.imageset/notebook.svg created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../../../crates/snowbound/assets/icons/notebook.svg | |
| \ No newline at end of file |
apps/ios/Snowbound/Assets.xcassets/NotebookBack.imageset/Contents.json created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | { | |
| 2 | "images" : [ | |
| 3 | { | |
| 4 | "filename" : "notebook-back.svg", | |
| 5 | "idiom" : "universal" | |
| 6 | } | |
| 7 | ], | |
| 8 | "info" : { | |
| 9 | "author" : "xcode", | |
| 10 | "version" : 1 | |
| 11 | }, | |
| 12 | "properties" : { | |
| 13 | "preserves-vector-representation" : true, | |
| 14 | "template-rendering-intent" : "original" | |
| 15 | } | |
| 16 | } |
apps/ios/Snowbound/Assets.xcassets/NotebookBack.imageset/notebook-back.svg created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../../../crates/snowbound/assets/icons/notebook-back.svg | |
| \ No newline at end of file |
apps/ios/Snowbound/Assets.xcassets/NotebookCover.imageset/Contents.json created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | { | |
| 2 | "images" : [ | |
| 3 | { | |
| 4 | "filename" : "notebook-cover.svg", | |
| 5 | "idiom" : "universal" | |
| 6 | } | |
| 7 | ], | |
| 8 | "info" : { | |
| 9 | "author" : "xcode", | |
| 10 | "version" : 1 | |
| 11 | }, | |
| 12 | "properties" : { | |
| 13 | "preserves-vector-representation" : true, | |
| 14 | "template-rendering-intent" : "template" | |
| 15 | } | |
| 16 | } |
apps/ios/Snowbound/Assets.xcassets/NotebookCover.imageset/notebook-cover.svg created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../../../crates/snowbound/assets/icons/notebook-cover.svg | |
| \ No newline at end of file |
apps/ios/Snowbound/Lists.swift+1-2| ... | ... | @@ -307,8 +307,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 307 | 307 | case .notebook(let id): |
| 308 | 308 | guard let notebook = notebook(id) else { break } |
| 309 | 309 | content.text = notebook.name |
| 310 | content.image = UIImage(systemName: "book.closed.fill") | |
| 311 | content.imageProperties.tintColor = notebook.color | |
| 310 | content.image = .notebook(notebook.color) | |
| 312 | 311 | var details: [String] = [] |
| 313 | 312 | if case .server(let server) = notebook.source { details.append(server.host) } |
| 314 | 313 | if let state = attention[id]?.state { |
apps/ios/Snowbound/Notebooks.swift+15-1| ... | ... | @@ -140,6 +140,20 @@ struct Tab: Decodable, Equatable { |
| 140 | 140 | var uiColor: UIColor { UIColor(rgb: color) } |
| 141 | 141 | } |
| 142 | 142 | |
| 143 | extension UIImage { | |
| 144 | /// A notebook's binder, the desktop's art, `side` points square: its cover in `color`, or | |
| 145 | /// the art's own orange. | |
| 146 | static func notebook(_ color: UIColor?, side: CGFloat = 28) -> UIImage { | |
| 147 | let frame = CGRect(x: 0, y: 0, width: side, height: side) | |
| 148 | let cover = color ?? UIColor(rgb: [0xf3, 0x9c, 0x28]) | |
| 149 | return UIGraphicsImageRenderer(size: frame.size).image { _ in | |
| 150 | UIImage(named: "NotebookBack")?.draw(in: frame) | |
| 151 | UIImage(named: "NotebookCover")?.withTintColor(cover, renderingMode: .alwaysOriginal).draw(in: frame) | |
| 152 | UIImage(named: "Notebook")?.draw(in: frame) | |
| 153 | }.withRenderingMode(.alwaysOriginal) | |
| 154 | } | |
| 155 | } | |
| 156 | ||
| 143 | 157 | extension UIViewController { |
| 144 | 158 | /// Tells the reader `title` and `message` in an alert they dismiss with OK. |
| 145 | 159 | func alert(_ title: String, _ message: String) { |
| ... | ... | @@ -175,7 +189,7 @@ final class Notebook { |
| 175 | 189 | private(set) var name: String |
| 176 | 190 | private(set) var handle: OpaquePointer? |
| 177 | 191 | private(set) var tabs: [Tab] = [] |
| 178 | /// The notebook's colour, which tints its book in the list; none where it has none. | |
| 192 | /// The notebook's colour, which its binder's cover takes in the list; none where it has none. | |
| 179 | 193 | private(set) var color: UIColor? |
| 180 | 194 | /// Why the notebook could not be opened, while it cannot. |
| 181 | 195 | private(set) var problem: String? |
apps/ios/Snowbound/Prototypes.swift+2-2| ... | ... | @@ -38,7 +38,7 @@ extension Prototype { |
| 38 | 38 | ) -> UIContentUnavailableConfiguration? { |
| 39 | 39 | guard Notebooks.all.isEmpty, !ICloud.signedIn || ICloud.documents != nil else { return nil } |
| 40 | 40 | var welcome = UIContentUnavailableConfiguration.empty() |
| 41 | welcome.image = UIImage(systemName: "book.closed") | |
| 41 | welcome.image = .notebook(nil, side: 64) | |
| 42 | 42 | welcome.text = "No Notebooks Yet" |
| 43 | 43 | welcome.secondaryText = |
| 44 | 44 | ICloud.documents != nil |
| ... | ... | @@ -286,7 +286,7 @@ enum QuickNote { |
| 286 | 286 | let notebooks = Notebooks.all.filter { $0.tabs.contains(where: \.readable) } |
| 287 | 287 | let choices = notebooks.map { notebook in |
| 288 | 288 | UIMenu( |
| 289 | title: notebook.name, image: UIImage(systemName: "book.closed"), | |
| 289 | title: notebook.name, image: .notebook(notebook.color), | |
| 290 | 290 | children: notebook.tabs.filter(\.readable).map { tab in |
| 291 | 291 | let chosen = Place(notebook: notebook.id, section: tab.path) |
| 292 | 292 | let action = UIAction( |
crates/draw/src/render/icon.rs+22-3| ... | ... | @@ -33,7 +33,7 @@ impl Palette { |
| 33 | 33 | /// Rasterizes 16×16 SVG sources over each other at a whole `size` of device pixels. Paths are filled and stroked with `#rrggbb`, `currentColor`, |
| 34 | 34 | /// which paints the linear `ink`, or two-stop `linearGradient`s with SVG's defaults |
| 35 | 35 | /// (`objectBoundingBox` unless `userSpaceOnUse`, `x1`..`y2`, stop offsets, pad spread; no |
| 36 | /// `gradientTransform`). `fill-opacity` and `stroke-opacity` apply, and strokes have round | |
| 36 | /// `gradientTransform`). `fill-opacity`, `stroke-opacity` and `stop-opacity` apply, and strokes have round | |
| 37 | 37 | /// caps and joins. A slot's paths keep the shading among their colours: the hue turn, |
| 38 | 38 | /// saturation scale and lightness shift that carry the mean of the slot's colours in a |
| 39 | 39 | /// source onto the palette's colour apply to each of them. A source's `image`s of PNG |
| ... | ... | @@ -141,7 +141,8 @@ pub(crate) fn rasterize(sources: &[&str], size: u32, ink: [f32; 3], palette: &Pa |
| 141 | 141 | let [x, y] = [index % side as usize, index / side as usize] |
| 142 | 142 | .map(|device| device as f32 + 0.5) |
| 143 | 143 | .map(|device| device * 16.0 / size); |
| 144 | over(pixel, color([x, y]), alpha); | |
| 144 | let color = color([x, y]); | |
| 145 | over(pixel, color, alpha * color[3]); | |
| 145 | 146 | } |
| 146 | 147 | } |
| 147 | 148 | } |
| ... | ... | @@ -287,8 +288,12 @@ impl Paint { |
| 287 | 288 | .children() |
| 288 | 289 | .filter(|node| node.has_tag_name("stop")) |
| 289 | 290 | .map(|node| { |
| 291 | let [red, green, blue, _] = hex(node.attribute("stop-color").unwrap()); | |
| 292 | let opacity = node | |
| 293 | .attribute("stop-opacity") | |
| 294 | .map_or(1.0, |value| value.parse().unwrap()); | |
| 290 | 295 | ( |
| 291 | hex(node.attribute("stop-color").unwrap()), | |
| 296 | [red, green, blue, opacity], | |
| 292 | 297 | node.attribute("offset") |
| 293 | 298 | .map_or(0.0, |offset| number(offset, 1.0)), |
| 294 | 299 | ) |
| ... | ... | @@ -567,6 +572,20 @@ mod tests { |
| 567 | 572 | assert_eq!(alpha(1, 8), 64); |
| 568 | 573 | } |
| 569 | 574 | |
| 575 | #[test] | |
| 576 | fn stop_opacity_fades_a_gradient() { | |
| 577 | const FADE: &str = r##"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><linearGradient id="g" x2="0" y2="1"><stop offset="0" stop-color="#ffffff"/><stop offset="1" stop-color="#ffffff" stop-opacity="0"/></linearGradient></defs><path d="M0 0H16V16H0Z" fill="url(#g)"/></svg>"##; | |
| 578 | let image = rasterize(&[FADE], 16, [1.0; 3], &Palette::default()); | |
| 579 | let alpha = |y: usize| image.data[(y * 16 + 8) * 4 + 3]; | |
| 580 | assert!( | |
| 581 | alpha(0) > 240 && alpha(15) < 15, | |
| 582 | "{} {}", | |
| 583 | alpha(0), | |
| 584 | alpha(15) | |
| 585 | ); | |
| 586 | assert!((i32::from(alpha(8)) - 120).abs() < 10, "{}", alpha(8)); | |
| 587 | } | |
| 588 | ||
| 570 | 589 | /// A 2×1 PNG, red then half-transparent blue, fits the icon's width, centred, and paints |
| 571 | 590 | /// beneath the paths of later sources. |
| 572 | 591 | #[test] |
crates/snowbound/assets/icons/notebook-back.svg created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> | |
| 2 | <defs> | |
| 3 | <linearGradient id="g0" x2="0" y2="1"><stop offset="0" stop-color="#ff8f80"/><stop offset="1" stop-color="#f03b30"/></linearGradient> | |
| 4 | <linearGradient id="g1" x2="0" y2="1"><stop offset="0" stop-color="#86e88a"/><stop offset="1" stop-color="#28b04a"/></linearGradient> | |
| 5 | <linearGradient id="g2" x2="0" y2="1"><stop offset="0" stop-color="#79c2ff"/><stop offset="1" stop-color="#1f7cf0"/></linearGradient> | |
| 6 | </defs> | |
| 7 | <path d="M12.1 3.75L14.4 3.75A0.6 0.6 0 0 1 15 4.35L15 5.65A0.6 0.6 0 0 1 14.4 6.25L12.1 6.25A0.6 0.6 0 0 1 11.5 5.65L11.5 4.35A0.6 0.6 0 0 1 12.1 3.75Z" fill="#000000" fill-opacity="0.16"/> | |
| 8 | <path d="M12.1 3H14.4A0.6 0.6 0 0 1 15 3.6V4.9A0.6 0.6 0 0 1 14.4 5.5H12.1A0.6 0.6 0 0 1 11.5 4.9V3.6A0.6 0.6 0 0 1 12.1 3Z" fill="url(#g0)" stroke="#c42418" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 9 | <path d="M12.1 7.25L14.4 7.25A0.6 0.6 0 0 1 15 7.85L15 9.15A0.6 0.6 0 0 1 14.4 9.75L12.1 9.75A0.6 0.6 0 0 1 11.5 9.15L11.5 7.85A0.6 0.6 0 0 1 12.1 7.25Z" fill="#000000" fill-opacity="0.16"/> | |
| 10 | <path d="M12.1 6.5H14.4A0.6 0.6 0 0 1 15 7.1V8.4A0.6 0.6 0 0 1 14.4 9H12.1A0.6 0.6 0 0 1 11.5 8.4V7.1A0.6 0.6 0 0 1 12.1 6.5Z" fill="url(#g1)" stroke="#1b8a36" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 11 | <path d="M12.1 10.75L14.4 10.75A0.6 0.6 0 0 1 15 11.35L15 12.65A0.6 0.6 0 0 1 14.4 13.25L12.1 13.25A0.6 0.6 0 0 1 11.5 12.65L11.5 11.35A0.6 0.6 0 0 1 12.1 10.75Z" fill="#000000" fill-opacity="0.16"/> | |
| 12 | <path class="accent" d="M12.1 10H14.4A0.6 0.6 0 0 1 15 10.6V11.9A0.6 0.6 0 0 1 14.4 12.5H12.1A0.6 0.6 0 0 1 11.5 11.9V10.6A0.6 0.6 0 0 1 12.1 10Z" fill="url(#g2)" stroke="#1560c4" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 13 | <path d="M3.5 2.25L11.5 2.25A1 1 0 0 1 12.5 3.25L12.5 14.25A1 1 0 0 1 11.5 15.25L3.5 15.25A1 1 0 0 1 2.5 14.25L2.5 3.25A1 1 0 0 1 3.5 2.25Z" fill="#000000" fill-opacity="0.16"/> | |
| 14 | </svg> |
crates/snowbound/assets/icons/notebook-cover.svg created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> | |
| 2 | <path d="M3.5 1.5H11.5A1 1 0 0 1 12.5 2.5V13.5A1 1 0 0 1 11.5 14.5H3.5A1 1 0 0 1 2.5 13.5V2.5A1 1 0 0 1 3.5 1.5Z" fill="currentColor" stroke="currentColor" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 3 | </svg> |
crates/snowbound/assets/icons/notebook.svg+5-16| ... | ... | @@ -1,21 +1,10 @@ |
| 1 | 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> |
| 2 | 2 | <defs> |
| 3 | <linearGradient id="g0" x2="0" y2="1"><stop offset="0" stop-color="#ff8f80"/><stop offset="1" stop-color="#f03b30"/></linearGradient> | |
| 4 | <linearGradient id="g1" x2="0" y2="1"><stop offset="0" stop-color="#86e88a"/><stop offset="1" stop-color="#28b04a"/></linearGradient> | |
| 5 | <linearGradient id="g2" x2="0" y2="1"><stop offset="0" stop-color="#79c2ff"/><stop offset="1" stop-color="#1f7cf0"/></linearGradient> | |
| 6 | <linearGradient id="g3" x2="0" y2="1"><stop offset="0" stop-color="#ffffff"/><stop offset="1" stop-color="#000000"/></linearGradient> | |
| 7 | <linearGradient id="g4" x2="0" y2="1"><stop offset="0" stop-color="#fffdf3"/><stop offset="1" stop-color="#fbeec7"/></linearGradient> | |
| 3 | <linearGradient id="g0" x2="0" y2="1"><stop offset="0" stop-color="#ffffff" stop-opacity="0.4"/><stop offset="1" stop-color="#ffffff" stop-opacity="0"/></linearGradient> | |
| 4 | <linearGradient id="g1" x2="0" y2="1"><stop offset="0" stop-color="#fffdf3"/><stop offset="1" stop-color="#fbeec7"/></linearGradient> | |
| 8 | 5 | </defs> |
| 9 | <path d="M12.1 3.75L14.4 3.75A0.6 0.6 0 0 1 15 4.35L15 5.65A0.6 0.6 0 0 1 14.4 6.25L12.1 6.25A0.6 0.6 0 0 1 11.5 5.65L11.5 4.35A0.6 0.6 0 0 1 12.1 3.75Z" fill="#000000" fill-opacity="0.16"/> | |
| 10 | <path d="M12.1 3H14.4A0.6 0.6 0 0 1 15 3.6V4.9A0.6 0.6 0 0 1 14.4 5.5H12.1A0.6 0.6 0 0 1 11.5 4.9V3.6A0.6 0.6 0 0 1 12.1 3Z" fill="url(#g0)" stroke="#c42418" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 11 | <path d="M12.1 7.25L14.4 7.25A0.6 0.6 0 0 1 15 7.85L15 9.15A0.6 0.6 0 0 1 14.4 9.75L12.1 9.75A0.6 0.6 0 0 1 11.5 9.15L11.5 7.85A0.6 0.6 0 0 1 12.1 7.25Z" fill="#000000" fill-opacity="0.16"/> | |
| 12 | <path d="M12.1 6.5H14.4A0.6 0.6 0 0 1 15 7.1V8.4A0.6 0.6 0 0 1 14.4 9H12.1A0.6 0.6 0 0 1 11.5 8.4V7.1A0.6 0.6 0 0 1 12.1 6.5Z" fill="url(#g1)" stroke="#1b8a36" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 13 | <path d="M12.1 10.75L14.4 10.75A0.6 0.6 0 0 1 15 11.35L15 12.65A0.6 0.6 0 0 1 14.4 13.25L12.1 13.25A0.6 0.6 0 0 1 11.5 12.65L11.5 11.35A0.6 0.6 0 0 1 12.1 10.75Z" fill="#000000" fill-opacity="0.16"/> | |
| 14 | <path class="accent" d="M12.1 10H14.4A0.6 0.6 0 0 1 15 10.6V11.9A0.6 0.6 0 0 1 14.4 12.5H12.1A0.6 0.6 0 0 1 11.5 11.9V10.6A0.6 0.6 0 0 1 12.1 10Z" fill="url(#g2)" stroke="#1560c4" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 15 | <path d="M3.5 2.25L11.5 2.25A1 1 0 0 1 12.5 3.25L12.5 14.25A1 1 0 0 1 11.5 15.25L3.5 15.25A1 1 0 0 1 2.5 14.25L2.5 3.25A1 1 0 0 1 3.5 2.25Z" fill="#000000" fill-opacity="0.16"/> | |
| 16 | <path d="M3.5 1.5H11.5A1 1 0 0 1 12.5 2.5V13.5A1 1 0 0 1 11.5 14.5H3.5A1 1 0 0 1 2.5 13.5V2.5A1 1 0 0 1 3.5 1.5Z" fill="currentColor" stroke="currentColor" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 17 | <path d="M3.5 1.5H11.5A1 1 0 0 1 12.5 2.5V13.5A1 1 0 0 1 11.5 14.5H3.5A1 1 0 0 1 2.5 13.5V2.5A1 1 0 0 1 3.5 1.5Z" fill="url(#g3)" fill-opacity="0.3" stroke="#000000" stroke-opacity="0.3" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 18 | <path d="M6 4H10A0.5 0.5 0 0 1 10.5 4.5V6.5A0.5 0.5 0 0 1 10 7H6A0.5 0.5 0 0 1 5.5 6.5V4.5A0.5 0.5 0 0 1 6 4Z" fill="url(#g4)"/> | |
| 19 | <path d="M3.5 13.5V3A0.5 0.5 0 0 1 4 2.5H11" fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="0.85"/> | |
| 6 | <path d="M3.5 1.5H11.5A1 1 0 0 1 12.5 2.5V13.5A1 1 0 0 1 11.5 14.5H3.5A1 1 0 0 1 2.5 13.5V2.5A1 1 0 0 1 3.5 1.5Z" fill="url(#g0)" stroke="#000000" stroke-opacity="0.22" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/> | |
| 7 | <path d="M6 4H10A0.5 0.5 0 0 1 10.5 4.5V6.5A0.5 0.5 0 0 1 10 7H6A0.5 0.5 0 0 1 5.5 6.5V4.5A0.5 0.5 0 0 1 6 4Z" fill="url(#g1)"/> | |
| 8 | <path d="M3.5 13.5V3A0.5 0.5 0 0 1 4 2.5H11" fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="0.6"/> | |
| 20 | 9 | <path d="M1.5 3.75H4.5M1.5 6.25H4.5M1.5 8.75H4.5M1.5 11.25H4.5M1.5 13.5H4.5" fill="none" stroke="#3b4350" stroke-width="1.1" stroke-linecap="round" stroke-linejoin="round"/> |
| 21 | 10 | </svg> |
crates/snowbound/src/art.rs+9-1| ... | ... | @@ -27,10 +27,18 @@ pub const INDENT: &[&str] = art!("icons/indent"); |
| 27 | 27 | pub const INFO: &[&str] = art!("icons/info"); |
| 28 | 28 | pub const ITALIC: &[&str] = art!("icons/italic"); |
| 29 | 29 | pub const LINK: &[&str] = art!("icons/link"); |
| 30 | pub const NOTEBOOK: &[&str] = art!("icons/notebook"); | |
| 30 | /// A notebook: its back and tabs, the cover `currentColor` paints, and what lies on the cover. | |
| 31 | /// iOS layers the same files. | |
| 32 | pub const NOTEBOOK: &[&str] = &[ | |
| 33 | include_str!("../assets/icons/notebook-back.svg"), | |
| 34 | include_str!("../assets/icons/notebook-cover.svg"), | |
| 35 | include_str!("../assets/icons/notebook.svg"), | |
| 36 | ]; | |
| 31 | 37 | /// A notebook iCloud Drive keeps: its glyph with a cloud at the corner. |
| 32 | 38 | pub const NOTEBOOK_ICLOUD: &[&str] = &[ |
| 33 | 39 | NOTEBOOK[0], |
| 40 | NOTEBOOK[1], | |
| 41 | NOTEBOOK[2], | |
| 34 | 42 | include_str!("../assets/icons/icloud-badge.svg"), |
| 35 | 43 | ]; |
| 36 | 44 | pub const NUMBERING: &[&str] = art!("icons/numbering"); |
crates/snowbound/src/main.rs+6-4| ... | ... | @@ -4474,10 +4474,12 @@ fn section_color(color: Option<u32>) -> [f32; 4] { |
| 4474 | 4474 | color.map_or(draw::srgb(0x8a, 0xa8, 0xe4), canvas::gpu::colorref) |
| 4475 | 4475 | } |
| 4476 | 4476 | |
| 4477 | /// The colour a notebook's glyph takes from its COLORREF, as OneNote's navigation bar tints | |
| 4478 | /// it; the glyph's own orange without one. | |
| 4479 | fn notebook_color(color: Option<u32>) -> [f32; 4] { | |
| 4480 | color.map_or(draw::srgb(0xf3, 0x9c, 0x28), canvas::gpu::colorref) | |
| 4477 | /// The colour a notebook's cover takes in `theme`: its COLORREF's hue at the shade a section's | |
| 4478 | /// takes, or the glyph's own orange without one. | |
| 4479 | fn notebook_color(theme: &Theme, color: Option<u32>) -> [f32; 4] { | |
| 4480 | color.map_or(draw::srgb(0xf3, 0x9c, 0x28), |color| { | |
| 4481 | theme.section(canvas::gpu::colorref(color)).accent | |
| 4482 | }) | |
| 4481 | 4483 | } |
| 4482 | 4484 | |
| 4483 | 4485 | /// OneNote's information bar for conflicting changes, clicked anywhere: above a page with |
crates/snowbound/src/menus.rs+1-1| ... | ... | @@ -578,7 +578,7 @@ impl State { |
| 578 | 578 | tint: place |
| 579 | 579 | .path |
| 580 | 580 | .is_empty() |
| 581 | .then(|| crate::notebook_color(library.color())), | |
| 581 | .then(|| crate::notebook_color(theme, library.color())), | |
| 582 | 582 | }) |
| 583 | 583 | .collect() |
| 584 | 584 | } |
crates/snowbound/src/palette.rs+2-2| ... | ... | @@ -97,7 +97,7 @@ impl State { |
| 97 | 97 | .map(|library| Row { |
| 98 | 98 | text: library.name.clone(), |
| 99 | 99 | icon: Some(art::NOTEBOOK), |
| 100 | tint: Some(crate::notebook_color(library.color())), | |
| 100 | tint: Some(crate::notebook_color(&self.ui.theme, library.color())), | |
| 101 | 101 | target: Some(Target::Notebook(Arc::clone(library))), |
| 102 | 102 | ..Row::default() |
| 103 | 103 | }), |
| ... | ... | @@ -288,7 +288,7 @@ impl State { |
| 288 | 288 | .to_owned(), |
| 289 | 289 | after: "Closed".to_owned(), |
| 290 | 290 | icon: Some(art::NOTEBOOK), |
| 291 | tint: Some(crate::notebook_color(None)), | |
| 291 | tint: Some(crate::notebook_color(&self.ui.theme, None)), | |
| 292 | 292 | target: Some(target), |
| 293 | 293 | ..Row::default() |
| 294 | 294 | }); |
crates/snowbound/src/properties.rs+1-1| ... | ... | @@ -169,7 +169,7 @@ impl State { |
| 169 | 169 | .map(|&(color, text)| Item { |
| 170 | 170 | text, |
| 171 | 171 | icon: Some(art::SWATCH), |
| 172 | tint: Some(crate::notebook_color(Some(color))), | |
| 172 | tint: Some(crate::notebook_color(&theme, Some(color))), | |
| 173 | 173 | current: Some(color) == shown, |
| 174 | 174 | ..Item::default() |
| 175 | 175 | }) |
crates/snowbound/src/sidebar.rs+5-2| ... | ... | @@ -613,7 +613,7 @@ impl Leading { |
| 613 | 613 | fn art(self, theme: &Theme) -> (&'static [&'static str], [f32; 4]) { |
| 614 | 614 | match self { |
| 615 | 615 | Self::Icon(icon) => (icon, theme.text), |
| 616 | Self::Notebook(icon, color) => (icon, crate::notebook_color(color)), | |
| 616 | Self::Notebook(icon, color) => (icon, crate::notebook_color(theme, color)), | |
| 617 | 617 | Self::Section(section) => (art::SECTION, theme.section(section).accent), |
| 618 | 618 | } |
| 619 | 619 | } |
| ... | ... | @@ -867,7 +867,10 @@ impl crate::State { |
| 867 | 867 | ..Spec::default() |
| 868 | 868 | }, |
| 869 | 869 | ); |
| 870 | let tint = crate::notebook_color(self.notebook().and_then(|library| library.color())); | |
| 870 | let tint = crate::notebook_color( | |
| 871 | &self.ui.theme, | |
| 872 | self.notebook().and_then(|library| library.color()), | |
| 873 | ); | |
| 871 | 874 | if ui::shell::tool_button(&mut self.ui, "button", art::NOTEBOOK, tint, None).clicked { |
| 872 | 875 | self.sidebar = !self.sidebar; |
| 873 | 876 | self.save_settings(); |
crates/snowbound/src/sync.rs+1-1| ... | ... | @@ -681,7 +681,7 @@ fn build(ui: &mut Ui, facts: &Facts) -> Picked { |
| 681 | 681 | size: [fill(), px(line)], |
| 682 | 682 | text: Some(facts.name), |
| 683 | 683 | icon: Some(art::NOTEBOOK), |
| 684 | tint: Some(crate::notebook_color(facts.color)), | |
| 684 | tint: Some(crate::notebook_color(&theme, facts.color)), | |
| 685 | 685 | bold: true, |
| 686 | 686 | overflow: Overflow::Ellipsis, |
| 687 | 687 | ..Spec::default() |