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

gtk: show placeholder message when rockbox daemon is not started

+147 -9
+75 -1
gtk/data/gtk/window.blp
··· 235 235 236 236 Overlay library_overlay { 237 237 Adw.ViewStack main_stack { 238 - 238 + 239 239 Adw.ViewStackPage albums_page { 240 240 name: "albums-page"; 241 241 title: _("Albums"); 242 242 child: ScrolledWindow albums_scrolled_window { 243 243 $Albums albums {} 244 + }; 245 + } 246 + 247 + Adw.ViewStackPage placeholder_page { 248 + name: "placeholder-page"; 249 + title: _("Rockbox"); 250 + child: Box placeholder { 251 + hexpand: true; 252 + vexpand: true; 253 + halign: center; 254 + valign: center; 255 + orientation: vertical; 256 + 257 + Image { 258 + resource: "/mg/tsirysndr/Rockbox/icons/jpg/disc.png"; 259 + width-request: 300; 260 + height-request: 300; 261 + margin-top: 20; 262 + } 263 + 264 + Label oops_message { 265 + label: _("Oops! Can't connect to the rockbox daemon."); 266 + halign: center; 267 + margin-bottom: 10; 268 + 269 + styles [ 270 + "bold" 271 + ] 272 + } 273 + 274 + Label { 275 + use-markup: true; 276 + label: _("Please make sure it's running."); 277 + halign: center; 278 + } 279 + 280 + Label { 281 + use-markup: true; 282 + label: _("Rockbox CLI installation instructions can be found at <a href='https://github.com/tsirysndr/rockbox-zig?tab=readme-ov-file#-installation'>https://github.com/tsirysndr/rockbox-zig?tab=readme-ov-file#-installation</a>"); 283 + halign: center; 284 + } 285 + 286 + Label { 287 + label: _("Execute the following command in your terminal to start the daemon:"); 288 + halign: center; 289 + } 290 + 291 + Adw.Clamp { 292 + maximum-size: 400; 293 + margin-top: 20; 294 + 295 + Adw.PreferencesGroup { 296 + Adw.ActionRow { 297 + title: _("rockbox start"); 298 + 299 + styles [ 300 + "monospace" 301 + ] 302 + 303 + Button { 304 + valign: center; 305 + icon-name: "edit-copy-symbolic"; 306 + tooltip-text: _("Copy"); 307 + action-name: "app.copy_command"; 308 + 309 + styles [ 310 + "transparent-button" 311 + ] 312 + } 313 + 314 + } 315 + } 316 + } 317 + 244 318 }; 245 319 } 246 320
gtk/data/icons/jpg/disc.png

This is a binary file and will not be displayed.

+1
gtk/data/rockbox.gresource.xml.in
··· 20 20 <file compressed="true" preprocess="xml-stripblanks">gtk/songs.ui</file> 21 21 <file compressed="true" preprocess="xml-stripblanks">gtk/window.ui</file> 22 22 <file>icons/jpg/albumart.jpg</file> 23 + <file>icons/jpg/disc.png</file> 23 24 </gresource> 24 25 <gresource prefix="@PREFIX@/icons/scalable/categories/"> 25 26 <file alias="artist-symbolic.svg">icons/svg/artist-symbolic.svg</file>
+71 -8
gtk/src/ui/window.rs
··· 1 1 use crate::api::rockbox::v1alpha1::library_service_client::LibraryServiceClient; 2 2 use crate::api::rockbox::v1alpha1::playback_service_client::PlaybackServiceClient; 3 + use crate::api::rockbox::v1alpha1::system_service_client::SystemServiceClient; 3 4 use crate::api::rockbox::v1alpha1::{ 4 - PlayAllTracksRequest, PlayLikedTracksRequest, ScanLibraryRequest, SearchRequest, SearchResponse, 5 + GetGlobalStatusRequest, GetGlobalStatusResponse, PlayAllTracksRequest, PlayLikedTracksRequest, 6 + ScanLibraryRequest, SearchRequest, SearchResponse, 5 7 }; 6 8 use crate::app::RbApplication; 7 9 use crate::config; ··· 139 141 pub media_control_bar: TemplateChild<MediaControls>, 140 142 #[template_child] 141 143 pub notice_no_results: TemplateChild<StatusPage>, 144 + #[template_child] 145 + pub placeholder_page: TemplateChild<ViewStackPage>, 142 146 143 147 pub show_sidebar: Cell<bool>, 144 148 pub state: glib::WeakRef<AppState>, 145 149 pub current_track: RefCell<Option<Track>>, 150 + pub show_placeholder: Cell<bool>, 146 151 } 147 152 148 153 #[glib::object_subclass] ··· 155 160 Self { 156 161 show_sidebar: Cell::new(true), 157 162 state: glib::WeakRef::new(), 163 + show_placeholder: Cell::new(false), 158 164 ..Default::default() 159 165 } 160 166 } ··· 212 218 self_.toggle_searchbar(); 213 219 }, 214 220 ); 221 + 222 + klass.install_action("app.copy_command", None, move |win, _action, _parameter| { 223 + const CMD: &str = "rockbox start"; 224 + win.clipboard().set_text(CMD); 225 + win.add_message_toast("Copied to clipboard"); 226 + }); 215 227 } 216 228 217 229 fn instance_init(obj: &subclass::InitializingObject<Self>) { ··· 222 234 impl ObjectImpl for RbApplicationWindow { 223 235 fn constructed(&self) { 224 236 self.parent_constructed(); 237 + 238 + self.verify_rockboxd(); 225 239 226 240 let weak_self = self.downgrade(); 227 241 self.albums_scrolled_window ··· 399 413 match label.as_str() { 400 414 "Albums" => { 401 415 let main_stack = self_.main_stack.get(); 402 - main_stack.set_visible_child_name("albums-page"); 416 + if !self_.show_placeholder.get() { 417 + main_stack.set_visible_child_name("albums-page"); 418 + } 403 419 let library_page = self_.library_page.get(); 404 420 library_page.set_title("Albums"); 405 421 state.new_navigation_from("Albums", "albums-page"); ··· 410 426 } 411 427 "Artists" => { 412 428 let main_stack = self_.main_stack.get(); 413 - main_stack.set_visible_child_name("artists-page"); 429 + if !self_.show_placeholder.get() { 430 + main_stack.set_visible_child_name("artists-page"); 431 + } 414 432 let library_page = self_.library_page.get(); 415 433 library_page.set_title("Artists"); 416 434 state.new_navigation_from("Artists", "artists-page"); ··· 421 439 } 422 440 "Songs" => { 423 441 let main_stack = self_.main_stack.get(); 424 - main_stack.set_visible_child_name("songs-page"); 442 + if !self_.show_placeholder.get() { 443 + main_stack.set_visible_child_name("songs-page"); 444 + } 425 445 let library_page = self_.library_page.get(); 426 446 library_page.set_title("Songs"); 427 447 state.new_navigation_from("Songs", "songs-page"); ··· 435 455 } 436 456 "Likes" => { 437 457 let main_stack = self_.main_stack.get(); 438 - main_stack.set_visible_child_name("likes-page"); 458 + if !self_.show_placeholder.get() { 459 + main_stack.set_visible_child_name("likes-page"); 460 + } 439 461 let library_page = self_.library_page.get(); 440 462 library_page.set_title("Likes"); 441 463 state.new_navigation_from("Likes", "likes-page"); ··· 459 481 } 460 482 "Files" => { 461 483 let main_stack = self_.main_stack.get(); 462 - main_stack.set_visible_child_name("files-page"); 484 + if !self_.show_placeholder.get() { 485 + main_stack.set_visible_child_name("files-page"); 486 + } 463 487 let library_page = self_.library_page.get(); 464 488 library_page.set_title("Files"); 465 489 state.new_navigation_from("Files", "files-page"); ··· 494 518 impl AdwApplicationWindowImpl for RbApplicationWindow {} 495 519 496 520 impl RbApplicationWindow { 521 + fn verify_rockboxd(&self) { 522 + let rt = tokio::runtime::Runtime::new().unwrap(); 523 + let global_status = rt.block_on(async { 524 + let url = build_url(); 525 + let mut client = SystemServiceClient::connect(url).await?; 526 + let response = client.get_global_status(GetGlobalStatusRequest {}).await?; 527 + Ok::<GetGlobalStatusResponse, Error>(response.into_inner()) 528 + }); 529 + 530 + match global_status { 531 + Ok(_) => { 532 + self.show_placeholder.set(false); 533 + self.media_control_bar.set_visible(true); 534 + } 535 + Err(_) => { 536 + let main_stack = self.main_stack.get(); 537 + main_stack.set_visible_child_name("placeholder-page"); 538 + self.show_placeholder.set(true); 539 + self.media_control_bar.set_visible(false); 540 + } 541 + } 542 + } 543 + 497 544 fn toggle_sidebar(&self) { 498 545 let current_state = self.show_sidebar.get(); 499 546 self.show_sidebar.set(!current_state); ··· 515 562 let library_page = self.library_page.get(); 516 563 let go_back_button = self.go_back_button.get(); 517 564 let state = self.state.upgrade().unwrap(); 518 - main_stack.set_visible_child_name("search-page"); 565 + 566 + if !self.show_placeholder.get() { 567 + main_stack.set_visible_child_name("search-page"); 568 + } 569 + 519 570 library_page.set_title("Search Results"); 520 571 go_back_button.set_visible(true); 521 572 state.push_navigation("Search", "search-page"); ··· 565 616 self.search.imp().artist_results.clear(false); 566 617 self.search.imp().track_results.clear(false); 567 618 self.search_entry.get().set_text(""); 619 + 620 + if self.show_placeholder.get() { 621 + main_stack.set_visible_child_name("placeholder-page"); 622 + } 568 623 } 569 624 570 625 if current_page.1 == "search-page" { ··· 573 628 state.set_search_mode(true); 574 629 } 575 630 576 - main_stack.set_visible_child_name(current_page.1.as_str()); 631 + if !self.show_placeholder.get() { 632 + main_stack.set_visible_child_name(current_page.1.as_str()); 633 + } 634 + 577 635 let library_page = self.library_page.get(); 578 636 library_page.set_title(current_page.0.as_str()); 579 637 ··· 931 989 album_results.imp().set_album_details(album_details.clone()); 932 990 933 991 window 992 + } 993 + 994 + pub fn add_message_toast(&self, message: &str) { 995 + let toast = adw::Toast::new(message); 996 + self.imp().toast_overlay.add_toast(toast); 934 997 } 935 998 } 936 999