A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

gtk: get album details and display all tracks and metadata

+319 -656
+1 -1
gtk/build.rs
··· 2 2 tonic_build::configure() 3 3 .out_dir("src/api") 4 4 .file_descriptor_set_path("src/api/rockbox_descriptor.bin") 5 - .compile( 5 + .compile_protos( 6 6 &[ 7 7 "proto/rockbox/v1alpha1/browse.proto", 8 8 "proto/rockbox/v1alpha1/library.proto",
+7 -214
gtk/data/gtk/album_details.blp
··· 19 19 valign: start; 20 20 width-request: 230; 21 21 height-request: 230; 22 - file: "/home/tsirysndr/.config/rockbox.org/covers/8cd770913d6057a716debac03873c78e.jpg"; 22 + resource: "/mg/tsirysndr/Rockbox/icons/jpg/albumart.jpg"; 23 23 } 24 24 25 25 styles [ ··· 34 34 valign: center; 35 35 36 36 Label album_title { 37 - label: "Leave a Whisper"; 37 + label: ""; 38 38 halign: start; 39 39 max-width-chars: 200; 40 40 wrap: true; ··· 48 48 } 49 49 50 50 Label album_artist { 51 - label: "Shinedown"; 51 + label: ""; 52 52 halign: start; 53 53 max-width-chars: 200; 54 54 wrap: true; ··· 61 61 } 62 62 63 63 Label album_tracks { 64 - label: "12 TRACKS"; 64 + label: ""; 65 65 halign: start; 66 66 margin-top: 25; 67 67 ··· 71 71 } 72 72 73 73 Label album_year { 74 - label: "2003"; 74 + label: ""; 75 75 halign: start; 76 76 margin-top: 5; 77 77 margin-bottom: 15; ··· 90 90 Button album_play_button { 91 91 tooltip-text: _("Play album"); 92 92 icon-name: "media-playback-start-symbolic"; 93 + action-name: "app.play-album"; 93 94 halign: center; 94 95 valign: center; 95 96 margin-end: 10; ··· 107 108 Button album_shuffle_button { 108 109 tooltip-text: _("Shuffle album"); 109 110 icon-name: "media-playlist-shuffle-symbolic"; 111 + action-name: "app.shuffle-album"; 110 112 halign: center; 111 113 valign: center; 112 114 margin-end: 0; ··· 149 151 margin-top: 15; 150 152 margin-bottom: 15; 151 153 selection-mode: none; 152 - 153 - Box { 154 - orientation: horizontal; 155 - spacing: 10; 156 - halign: fill; 157 - valign: center; 158 - hexpand: true; 159 - margin-bottom: 10; 160 - margin-top: 10; 161 - margin-start: 10; 162 - margin-end: 10; 163 - 164 - Label { 165 - label: "01"; 166 - halign: start; 167 - valign: center; 168 - margin-end: 10; 169 - 170 - styles [ 171 - "track-number" 172 - ] 173 - } 174 - 175 - Box { 176 - hexpand: true; 177 - valign: center; 178 - halign: fill; 179 - orientation: vertical; 180 - 181 - Label { 182 - label: "Fly from the Inside"; 183 - halign: start; 184 - valign: center; 185 - hexpand: true; 186 - margin-bottom: 3; 187 - 188 - styles [ 189 - "track-title" 190 - ] 191 - } 192 - 193 - Label { 194 - label: "Shinedown"; 195 - halign: start; 196 - valign: center; 197 - hexpand: true; 198 - 199 - styles [ 200 - "track-artist" 201 - ] 202 - } 203 - } 204 - 205 - Label { 206 - label: "3:55"; 207 - halign: end; 208 - valign: center; 209 - 210 - styles [ 211 - "track-duration" 212 - ] 213 - } 214 - 215 - Button { 216 - tooltip-text: _("Like"); 217 - child: Image { 218 - icon-name: "heart-outline-symbolic"; 219 - pixel-size: 24; 220 - }; 221 - halign: center; 222 - valign: center; 223 - margin-end: 0; 224 - margin-start: 0; 225 - margin-top: 0; 226 - margin-bottom: 0; 227 - width-request: 40; 228 - height-request: 40; 229 - 230 - styles [ 231 - "transparent-button" 232 - ] 233 - } 234 - 235 - Button { 236 - tooltip-text: _("More"); 237 - 238 - child: Image { 239 - icon-name: "options-symbolic"; 240 - pixel-size: 24; 241 - }; 242 - 243 - halign: center; 244 - valign: center; 245 - margin-end: 0; 246 - margin-start: 0; 247 - margin-top: 0; 248 - margin-bottom: 0; 249 - width-request: 40; 250 - height-request: 40; 251 - 252 - styles [ 253 - "transparent-button" 254 - ] 255 - } 256 - } 257 - 258 - Box { 259 - orientation: horizontal; 260 - spacing: 10; 261 - halign: fill; 262 - valign: center; 263 - hexpand: true; 264 - margin-bottom: 10; 265 - margin-top: 10; 266 - margin-start: 10; 267 - margin-end: 10; 268 - 269 - Label { 270 - label: "02"; 271 - halign: start; 272 - valign: center; 273 - margin-end: 10; 274 - 275 - styles [ 276 - "track-number" 277 - ] 278 - } 279 - 280 - Box { 281 - hexpand: true; 282 - halign: fill; 283 - valign: center; 284 - orientation: vertical; 285 - 286 - Label { 287 - label: "Left Out"; 288 - halign: start; 289 - valign: center; 290 - hexpand: true; 291 - margin-bottom: 3; 292 - 293 - styles [ 294 - "track-title" 295 - ] 296 - } 297 - 298 - Label { 299 - label: "Shinedown"; 300 - halign: start; 301 - valign: center; 302 - hexpand: true; 303 - 304 - styles [ 305 - "track-artist" 306 - ] 307 - } 308 - 309 - } 310 - 311 - Label track_duration { 312 - label: "3:59"; 313 - halign: end; 314 - valign: center; 315 - 316 - styles [ 317 - "track-duration" 318 - ] 319 - } 320 - 321 - Button { 322 - tooltip-text: _("Like"); 323 - child: Image { 324 - icon-name: "heart-outline-symbolic"; 325 - pixel-size: 24; 326 - }; 327 - halign: center; 328 - valign: center; 329 - margin-end: 0; 330 - margin-start: 0; 331 - margin-top: 0; 332 - margin-bottom: 0; 333 - width-request: 40; 334 - height-request: 40; 335 - 336 - styles [ 337 - "transparent-button" 338 - ] 339 - } 340 - 341 - Button { 342 - tooltip-text: _("More"); 343 - child: Image { 344 - icon-name: "options-symbolic"; 345 - pixel-size: 24; 346 - }; 347 - halign: center; 348 - valign: center; 349 - margin-end: 0; 350 - margin-start: 0; 351 - margin-top: 0; 352 - margin-bottom: 0; 353 - width-request: 40; 354 - height-request: 40; 355 - 356 - styles [ 357 - "transparent-button" 358 - ] 359 - } 360 - } 361 154 362 155 styles [ 363 156 "album-track-list"
-197
gtk/data/gtk/likes.blp
··· 7 7 margin-bottom: 15; 8 8 selection-mode: none; 9 9 10 - Box { 11 - orientation: horizontal; 12 - spacing: 10; 13 - halign: fill; 14 - valign: center; 15 - hexpand: true; 16 - margin-bottom: 0; 17 - margin-top: 0; 18 - margin-start: 10; 19 - margin-end: 10; 20 - 21 - Label { 22 - label: "01"; 23 - halign: start; 24 - valign: center; 25 - margin-end: 10; 26 - 27 - styles [ 28 - "track-number" 29 - ] 30 - } 31 - 32 - Box { 33 - margin-end: 10; 34 - Image { 35 - width-request: 50; 36 - height-request: 50; 37 - file: "/home/tsirysndr/.config/rockbox.org/covers/8cd770913d6057a716debac03873c78e.jpg"; 38 - } 39 - 40 - styles [ 41 - "media-album-art" 42 - ] 43 - } 44 - 45 - Box { 46 - hexpand: true; 47 - valign: center; 48 - halign: fill; 49 - orientation: vertical; 50 - 51 - Label { 52 - label: "Fly from the Inside"; 53 - halign: start; 54 - valign: center; 55 - hexpand: true; 56 - margin-bottom: 3; 57 - 58 - styles [ 59 - "track-title" 60 - ] 61 - } 62 - 63 - Label { 64 - label: "Shinedown"; 65 - halign: start; 66 - valign: center; 67 - hexpand: true; 68 - 69 - styles [ 70 - "track-artist" 71 - ] 72 - } 73 - } 74 - 75 - Label { 76 - label: "3:55"; 77 - halign: end; 78 - valign: center; 79 - 80 - styles [ 81 - "track-duration" 82 - ] 83 - } 84 - 85 - Button { 86 - tooltip-text: _("More"); 87 - 88 - child: Image { 89 - icon-name: "options-symbolic"; 90 - pixel-size: 24; 91 - }; 92 - 93 - halign: center; 94 - valign: center; 95 - margin-end: 0; 96 - margin-start: 0; 97 - margin-top: 0; 98 - margin-bottom: 0; 99 - width-request: 40; 100 - height-request: 40; 101 - 102 - styles [ 103 - "transparent-button" 104 - ] 105 - } 106 - } 107 - 108 - Box { 109 - orientation: horizontal; 110 - spacing: 10; 111 - halign: fill; 112 - valign: center; 113 - hexpand: true; 114 - margin-bottom: 0; 115 - margin-top: 0; 116 - margin-start: 10; 117 - margin-end: 10; 118 - 119 - Label { 120 - label: "02"; 121 - halign: start; 122 - valign: center; 123 - margin-end: 10; 124 - 125 - styles [ 126 - "track-number" 127 - ] 128 - } 129 - 130 - Box { 131 - margin-end: 10; 132 - Image { 133 - width-request: 50; 134 - height-request: 50; 135 - file: "/home/tsirysndr/.config/rockbox.org/covers/8cd770913d6057a716debac03873c78e.jpg"; 136 - } 137 - 138 - styles [ 139 - "media-album-art" 140 - ] 141 - } 142 - 143 - Box { 144 - hexpand: true; 145 - halign: fill; 146 - valign: center; 147 - orientation: vertical; 148 - 149 - Label { 150 - label: "Left Out"; 151 - halign: start; 152 - valign: center; 153 - hexpand: true; 154 - margin-bottom: 3; 155 - 156 - styles [ 157 - "track-title" 158 - ] 159 - } 160 - 161 - Label { 162 - label: "Shinedown"; 163 - halign: start; 164 - valign: center; 165 - hexpand: true; 166 - 167 - styles [ 168 - "track-artist" 169 - ] 170 - } 171 - 172 - } 173 - 174 - Label track_duration { 175 - label: "3:59"; 176 - halign: end; 177 - valign: center; 178 - 179 - styles [ 180 - "track-duration" 181 - ] 182 - } 183 - 184 - Button { 185 - tooltip-text: _("More"); 186 - 187 - child: Image { 188 - icon-name: "options-symbolic"; 189 - pixel-size: 24; 190 - }; 191 - 192 - halign: center; 193 - valign: center; 194 - margin-end: 0; 195 - margin-start: 0; 196 - margin-top: 0; 197 - margin-bottom: 0; 198 - width-request: 40; 199 - height-request: 40; 200 - 201 - styles [ 202 - "transparent-button" 203 - ] 204 - } 205 - } 206 - 207 10 styles [ 208 11 "album-track-list" 209 12 ]
+3
gtk/data/gtk/media_controls.blp
··· 80 80 } 81 81 82 82 Box { 83 + margin-start: 10; 84 + 83 85 Image album_art { 84 86 width-request: 60; 85 87 height-request: 60; ··· 98 100 spacing: 0; 99 101 valign: center; 100 102 margin-top: 17; 103 + margin-end: 10; 101 104 102 105 Box media_current_song_box { 103 106 hexpand: true;
+123
gtk/data/gtk/song.blp
··· 1 + using Gtk 4.0; 2 + 3 + template $Song : Box { 4 + orientation: horizontal; 5 + spacing: 10; 6 + halign: fill; 7 + valign: center; 8 + hexpand: true; 9 + margin-bottom: 10; 10 + margin-top: 10; 11 + margin-start: 10; 12 + margin-end: 10; 13 + 14 + Label track_number { 15 + label: ""; 16 + halign: start; 17 + valign: center; 18 + margin-end: 10; 19 + 20 + styles [ 21 + "track-number" 22 + ] 23 + } 24 + 25 + Box { 26 + margin-end: 10; 27 + visible: false; 28 + 29 + Image album_art { 30 + width-request: 50; 31 + height-request: 50; 32 + resource: "/mg/tsirysndr/Rockbox/icons/jpg/albumart.jpg"; 33 + } 34 + 35 + styles [ 36 + "media-album-art" 37 + ] 38 + } 39 + 40 + Box { 41 + hexpand: true; 42 + valign: center; 43 + halign: fill; 44 + orientation: vertical; 45 + 46 + Label track_title { 47 + label: ""; 48 + halign: start; 49 + valign: center; 50 + hexpand: true; 51 + margin-bottom: 3; 52 + 53 + styles [ 54 + "track-title" 55 + ] 56 + } 57 + 58 + Label artist { 59 + label: ""; 60 + halign: start; 61 + valign: center; 62 + hexpand: true; 63 + 64 + styles [ 65 + "track-artist" 66 + ] 67 + } 68 + } 69 + 70 + Label track_duration { 71 + label: ""; 72 + halign: end; 73 + valign: center; 74 + 75 + styles [ 76 + "track-duration" 77 + ] 78 + } 79 + 80 + Button { 81 + tooltip-text: _("Like"); 82 + child: Image { 83 + icon-name: "heart-outline-symbolic"; 84 + pixel-size: 24; 85 + }; 86 + action-name: "app.like-song"; 87 + halign: center; 88 + valign: center; 89 + margin-end: 0; 90 + margin-start: 0; 91 + margin-top: 0; 92 + margin-bottom: 0; 93 + width-request: 40; 94 + height-request: 40; 95 + 96 + styles [ 97 + "transparent-button" 98 + ] 99 + } 100 + 101 + Button { 102 + tooltip-text: _("More"); 103 + 104 + child: Image { 105 + icon-name: "options-symbolic"; 106 + pixel-size: 24; 107 + }; 108 + 109 + halign: center; 110 + valign: center; 111 + margin-end: 0; 112 + margin-start: 0; 113 + margin-top: 0; 114 + margin-bottom: 0; 115 + width-request: 40; 116 + height-request: 40; 117 + 118 + styles [ 119 + "transparent-button" 120 + ] 121 + } 122 + } 123 +
-233
gtk/data/gtk/songs.blp
··· 7 7 margin-bottom: 15; 8 8 selection-mode: none; 9 9 10 - Box { 11 - orientation: horizontal; 12 - spacing: 10; 13 - halign: fill; 14 - valign: center; 15 - hexpand: true; 16 - margin-bottom: 0; 17 - margin-top: 0; 18 - margin-start: 10; 19 - margin-end: 10; 20 - 21 - Label { 22 - label: "01"; 23 - halign: start; 24 - valign: center; 25 - margin-end: 10; 26 - 27 - styles [ 28 - "track-number" 29 - ] 30 - } 31 - 32 - Box { 33 - margin-end: 10; 34 - Image { 35 - width-request: 50; 36 - height-request: 50; 37 - file: "/home/tsirysndr/.config/rockbox.org/covers/8cd770913d6057a716debac03873c78e.jpg"; 38 - } 39 - 40 - styles [ 41 - "media-album-art" 42 - ] 43 - } 44 - 45 - Box { 46 - hexpand: true; 47 - valign: center; 48 - halign: fill; 49 - orientation: vertical; 50 - 51 - Label { 52 - label: "Fly from the Inside"; 53 - halign: start; 54 - valign: center; 55 - hexpand: true; 56 - margin-bottom: 3; 57 - 58 - styles [ 59 - "track-title" 60 - ] 61 - } 62 - 63 - Label { 64 - label: "Shinedown"; 65 - halign: start; 66 - valign: center; 67 - hexpand: true; 68 - 69 - styles [ 70 - "track-artist" 71 - ] 72 - } 73 - } 74 - 75 - Label { 76 - label: "3:55"; 77 - halign: end; 78 - valign: center; 79 - 80 - styles [ 81 - "track-duration" 82 - ] 83 - } 84 - 85 - Button { 86 - tooltip-text: _("Like"); 87 - child: Image { 88 - icon-name: "heart-outline-symbolic"; 89 - pixel-size: 24; 90 - }; 91 - halign: center; 92 - valign: center; 93 - margin-end: 0; 94 - margin-start: 0; 95 - margin-top: 0; 96 - margin-bottom: 0; 97 - width-request: 40; 98 - height-request: 40; 99 - 100 - styles [ 101 - "transparent-button" 102 - ] 103 - } 104 - 105 - Button { 106 - tooltip-text: _("More"); 107 - child: Image { 108 - icon-name: "options-symbolic"; 109 - pixel-size: 24; 110 - }; 111 - halign: center; 112 - valign: center; 113 - margin-end: 0; 114 - margin-start: 0; 115 - margin-top: 0; 116 - margin-bottom: 0; 117 - width-request: 40; 118 - height-request: 40; 119 - 120 - styles [ 121 - "transparent-button" 122 - ] 123 - } 124 - } 125 - 126 - Box { 127 - orientation: horizontal; 128 - spacing: 10; 129 - halign: fill; 130 - valign: center; 131 - hexpand: true; 132 - margin-bottom: 0; 133 - margin-top: 0; 134 - margin-start: 10; 135 - margin-end: 10; 136 - 137 - Label { 138 - label: "02"; 139 - halign: start; 140 - valign: center; 141 - margin-end: 10; 142 - 143 - styles [ 144 - "track-number" 145 - ] 146 - } 147 - 148 - Box { 149 - margin-end: 10; 150 - Image { 151 - width-request: 50; 152 - height-request: 50; 153 - file: "/home/tsirysndr/.config/rockbox.org/covers/8cd770913d6057a716debac03873c78e.jpg"; 154 - } 155 - 156 - styles [ 157 - "media-album-art" 158 - ] 159 - } 160 - 161 - Box { 162 - hexpand: true; 163 - halign: fill; 164 - valign: center; 165 - orientation: vertical; 166 - 167 - Label { 168 - label: "Left Out"; 169 - halign: start; 170 - valign: center; 171 - hexpand: true; 172 - margin-bottom: 3; 173 - 174 - styles [ 175 - "track-title" 176 - ] 177 - } 178 - 179 - Label { 180 - label: "Shinedown"; 181 - halign: start; 182 - valign: center; 183 - hexpand: true; 184 - 185 - styles [ 186 - "track-artist" 187 - ] 188 - } 189 - 190 - } 191 - 192 - Label track_duration { 193 - label: "3:59"; 194 - halign: end; 195 - valign: center; 196 - 197 - styles [ 198 - "track-duration" 199 - ] 200 - } 201 - 202 - Button { 203 - tooltip-text: _("Like"); 204 - child: Image { 205 - icon-name: "heart-outline-symbolic"; 206 - pixel-size: 24; 207 - }; 208 - halign: center; 209 - valign: center; 210 - margin-end: 0; 211 - margin-start: 0; 212 - margin-top: 0; 213 - margin-bottom: 0; 214 - width-request: 40; 215 - height-request: 40; 216 - 217 - styles [ 218 - "transparent-button" 219 - ] 220 - } 221 - 222 - Button { 223 - tooltip-text: _("More"); 224 - child: Image { 225 - icon-name: "options-symbolic"; 226 - pixel-size: 24; 227 - }; 228 - halign: center; 229 - valign: center; 230 - margin-end: 0; 231 - margin-start: 0; 232 - margin-top: 0; 233 - margin-bottom: 0; 234 - width-request: 40; 235 - height-request: 40; 236 - 237 - styles [ 238 - "transparent-button" 239 - ] 240 - } 241 - } 242 - 243 10 styles [ 244 11 "album-track-list" 245 12 ]
+2
gtk/src/ui/mod.rs
··· 1 1 pub mod media_controls; 2 2 pub mod pages; 3 3 pub mod window; 4 + pub mod song; 5 +
+94 -4
gtk/src/ui/pages/album_details.rs
··· 1 + use crate::api::rockbox::v1alpha1::library_service_client::LibraryServiceClient; 2 + use crate::api::rockbox::v1alpha1::{Album, GetAlbumRequest}; 3 + use crate::time::format_milliseconds; 4 + use crate::ui::song::Song; 5 + use adw::prelude::*; 1 6 use adw::subclass::prelude::*; 7 + use anyhow::Error; 2 8 use glib::subclass; 3 9 use gtk::glib; 4 - use gtk::CompositeTemplate; 10 + use gtk::{CompositeTemplate, Image, Label, ListBox}; 11 + use std::{env, thread}; 5 12 6 13 mod imp { 7 14 ··· 9 16 10 17 #[derive(Debug, Default, CompositeTemplate)] 11 18 #[template(file = "../gtk/album_details.ui")] 12 - pub struct AlbumDetails {} 19 + pub struct AlbumDetails { 20 + #[template_child] 21 + pub album_cover: TemplateChild<Image>, 22 + #[template_child] 23 + pub album_title: TemplateChild<Label>, 24 + #[template_child] 25 + pub album_artist: TemplateChild<Label>, 26 + #[template_child] 27 + pub album_tracks: TemplateChild<Label>, 28 + #[template_child] 29 + pub album_year: TemplateChild<Label>, 30 + #[template_child] 31 + pub album_track_list: TemplateChild<ListBox>, 32 + } 13 33 14 34 #[glib::object_subclass] 15 35 impl ObjectSubclass for AlbumDetails { ··· 19 39 20 40 fn class_init(klass: &mut Self::Class) { 21 41 Self::bind_template(klass); 42 + 43 + klass.install_action( 44 + "app.play-album", 45 + None, 46 + move |_album_details, _action, _target| {}, 47 + ); 48 + 49 + klass.install_action( 50 + "app.shuffle-album", 51 + None, 52 + move |_album_details, _action, _target| {}, 53 + ); 22 54 } 23 55 24 56 fn instance_init(obj: &subclass::InitializingObject<Self>) { ··· 34 66 35 67 impl WidgetImpl for AlbumDetails {} 36 68 impl BoxImpl for AlbumDetails {} 69 + 70 + impl AlbumDetails { 71 + pub fn load_album(&self, id: &str) { 72 + let id = id.to_string(); 73 + let handle = thread::spawn(move || { 74 + let rt = tokio::runtime::Runtime::new().unwrap(); 75 + rt.block_on(async { 76 + let url = build_url(); 77 + let mut client = LibraryServiceClient::connect(url).await?; 78 + let response = client.get_album(GetAlbumRequest { id }).await?.into_inner(); 79 + Ok::<Option<Album>, Error>(response.album) 80 + }) 81 + }); 82 + 83 + if let Ok(Ok(Some(album))) = handle.join() { 84 + self.album_title.set_text(&album.title); 85 + self.album_artist.set_text(&album.artist); 86 + self.album_year.set_text(&format!("{}", album.year)); 87 + self.album_tracks 88 + .set_text(&format!("{} TRACKS", album.tracks.len())); 89 + 90 + match album.album_art { 91 + Some(filename) => { 92 + let home = std::env::var("HOME").unwrap(); 93 + let path = format!("{}/.config/rockbox.org/covers/{}", home, filename); 94 + self.album_cover.set_from_file(Some(&path)); 95 + } 96 + None => { 97 + self.album_cover 98 + .set_resource(Some("/mg/tsirysndr/Rockbox/icons/jpg/albumart.jpg")); 99 + } 100 + } 101 + 102 + let album_track_list = self.album_track_list.clone(); 103 + while let Some(row) = album_track_list.first_child() { 104 + album_track_list.remove(&row); 105 + } 106 + 107 + for track in album.tracks { 108 + let song = Song::new(); 109 + song.imp() 110 + .track_number 111 + .set_text(&format!("{}", track.track_number)); 112 + song.imp().track_title.set_text(&track.title); 113 + song.imp().artist.set_text(&track.artist); 114 + song.imp() 115 + .track_duration 116 + .set_text(&format_milliseconds(track.length as u64)); 117 + self.album_track_list.append(&song); 118 + } 119 + } 120 + } 121 + } 122 + } 123 + 124 + fn build_url() -> String { 125 + let host = env::var("ROCKBOX_HOST").unwrap_or_else(|_| "localhost".to_string()); 126 + let port = env::var("ROCKBOX_PORT").unwrap_or_else(|_| "6061".to_string()); 127 + 128 + format!("tcp://{}:{}", host, port) 37 129 } 38 130 39 131 glib::wrapper! { ··· 46 138 pub fn new() -> Self { 47 139 glib::Object::new() 48 140 } 49 - 50 - pub fn load_album(&self, id: &str) {} 51 141 }
+13 -3
gtk/src/ui/pages/albums.rs
··· 11 11 use gtk::CompositeTemplate; 12 12 use gtk::{glib, Box, FlowBox, Image, Label, Orientation}; 13 13 use std::cell::RefCell; 14 + use crate::ui::pages::album_details::AlbumDetails; 14 15 15 16 mod imp { 16 17 ··· 24 25 pub main_stack: RefCell<Option<adw::ViewStack>>, 25 26 pub library_page: RefCell<Option<adw::NavigationPage>>, 26 27 pub go_back_button: RefCell<Option<gtk::Button>>, 28 + pub album_details: RefCell<Option<AlbumDetails>>, 27 29 } 28 30 29 31 #[glib::object_subclass] ··· 77 79 *self.go_back_button.borrow_mut() = Some(go_back_button); 78 80 } 79 81 82 + pub fn set_album_details(&self, album_details: AlbumDetails) { 83 + *self.album_details.borrow_mut() = Some(album_details); 84 + } 85 + 80 86 pub fn add_picture_to_library( 81 87 &self, 88 + album_id: &str, 82 89 filename: Option<String>, 83 90 title: &str, 84 91 artist: &str, ··· 95 102 let image_container = Box::new(Orientation::Vertical, 0); 96 103 97 104 let self_weak = self.downgrade(); 98 - let title_ = title.to_string(); 105 + let album_id = album_id.to_string(); 99 106 100 107 let gesture = gtk::GestureClick::new(); 101 108 gesture.connect_released(move |_, _, _, _| { ··· 104 111 None => return, 105 112 }; 106 113 let obj = self_.obj(); 107 - obj.navigate_to_details(&title_); 114 + obj.navigate_to_details(&album_id); 115 + obj.imp().album_details.borrow().as_ref().unwrap().imp().load_album(&album_id); 108 116 }); 109 117 110 118 image_container.append(&image); 111 - image_container.add_controller(gesture); 119 + image_container.add_controller(gesture.clone()); 112 120 image_container.add_css_class("rounded-image"); 113 121 114 122 let title = Label::new(Some(title)); ··· 116 124 title.set_max_width_chars(23); 117 125 title.add_css_class("album-label"); 118 126 title.set_halign(gtk::Align::Start); 127 + title.add_controller(gesture); 119 128 120 129 let artist = Label::new(Some(artist)); 121 130 artist.set_ellipsize(EllipsizeMode::End); ··· 190 199 if let Ok(albums) = handle.join().unwrap() { 191 200 for album in albums.albums { 192 201 self.imp().add_picture_to_library( 202 + &album.id, 193 203 album.album_art, 194 204 &album.title, 195 205 &album.artist,
+5 -2
gtk/src/ui/pages/likes.rs
··· 1 1 use adw::subclass::prelude::*; 2 2 use glib::subclass; 3 3 use gtk::glib; 4 - use gtk::CompositeTemplate; 4 + use gtk::{CompositeTemplate, ListBox}; 5 5 6 6 mod imp { 7 7 ··· 9 9 10 10 #[derive(Debug, Default, CompositeTemplate)] 11 11 #[template(file = "../gtk/likes.ui")] 12 - pub struct Likes {} 12 + pub struct Likes { 13 + #[template_child] 14 + pub tracks: TemplateChild<ListBox>, 15 + } 13 16 14 17 #[glib::object_subclass] 15 18 impl ObjectSubclass for Likes {
+5 -2
gtk/src/ui/pages/songs.rs
··· 1 1 use adw::subclass::prelude::*; 2 2 use glib::subclass; 3 3 use gtk::glib; 4 - use gtk::CompositeTemplate; 4 + use gtk::{CompositeTemplate, ListBox}; 5 5 6 6 mod imp { 7 7 ··· 9 9 10 10 #[derive(Debug, Default, CompositeTemplate)] 11 11 #[template(file = "../gtk/songs.ui")] 12 - pub struct Songs {} 12 + pub struct Songs { 13 + #[template_child] 14 + pub tracks: TemplateChild<ListBox>, 15 + } 13 16 14 17 #[glib::object_subclass] 15 18 impl ObjectSubclass for Songs {
+64
gtk/src/ui/song.rs
··· 1 + use adw::subclass::prelude::*; 2 + use glib::subclass; 3 + use gtk::glib; 4 + use gtk::{CompositeTemplate, Image, Label}; 5 + 6 + mod imp { 7 + 8 + use super::*; 9 + 10 + #[derive(Debug, Default, CompositeTemplate)] 11 + #[template(file = "./gtk/song.ui")] 12 + pub struct Song { 13 + #[template_child] 14 + pub album_art: TemplateChild<Image>, 15 + #[template_child] 16 + pub track_number: TemplateChild<Label>, 17 + #[template_child] 18 + pub track_title: TemplateChild<Label>, 19 + #[template_child] 20 + pub artist: TemplateChild<Label>, 21 + #[template_child] 22 + pub track_duration: TemplateChild<Label>, 23 + } 24 + 25 + #[glib::object_subclass] 26 + impl ObjectSubclass for Song { 27 + const NAME: &'static str = "Song"; 28 + type ParentType = gtk::Box; 29 + type Type = super::Song; 30 + 31 + fn class_init(klass: &mut Self::Class) { 32 + Self::bind_template(klass); 33 + 34 + klass.install_action("app.like-song", None, move |_song, _action, _target| { 35 + 36 + }); 37 + } 38 + 39 + fn instance_init(obj: &subclass::InitializingObject<Self>) { 40 + obj.init_template(); 41 + } 42 + } 43 + 44 + impl ObjectImpl for Song { 45 + fn constructed(&self) { 46 + self.parent_constructed(); 47 + } 48 + } 49 + 50 + impl WidgetImpl for Song {} 51 + impl BoxImpl for Song {} 52 + } 53 + 54 + glib::wrapper! { 55 + pub struct Song(ObjectSubclass<imp::Song>) 56 + @extends gtk::Widget, gtk::Box; 57 + } 58 + 59 + #[gtk::template_callbacks] 60 + impl Song { 61 + pub fn new() -> Self { 62 + glib::Object::new() 63 + } 64 + }
+2
gtk/src/ui/window.rs
··· 234 234 let main_stack = window.imp().main_stack.get(); 235 235 let library_page = window.imp().library_page.get(); 236 236 let albums = window.imp().albums.get(); 237 + let album_details = window.imp().album_details.get(); 237 238 238 239 albums.imp().set_main_stack(main_stack.clone()); 239 240 albums.imp().set_library_page(library_page.clone()); 240 241 albums 241 242 .imp() 242 243 .set_go_back_button(window.imp().go_back_button.get().clone()); 244 + albums.imp().set_album_details(album_details.clone()); 243 245 244 246 window 245 247 }