#![windows_subsystem = "windows"] use gio::prelude::*; use gtk::prelude::*; use gtk::Orientation; use rodio::{Sink, Source}; use std::env::args; use std::fs::File; use std::io::BufReader; use std::path::PathBuf; use std::process::Command; const SPACING: i32 = 16; #[cfg(target_os = "windows")] fn file_manager() -> &'static str { "explorer" } #[cfg(not(target_os = "windows"))] fn file_manager() -> &'static str { "xdg-open" } fn error_popup(message: &str) { let popup = gtk::Window::new(gtk::WindowType::Toplevel); popup.set_title("error"); popup.set_border_width(SPACING as u32); popup.set_position(gtk::WindowPosition::Center); popup.set_default_size(256, 64); popup.set_type_hint(gdk::WindowTypeHint::Dialog); popup.set_resizable(false); let message = gtk::Label::new(Some(message)); popup.add(&message); popup.show_all(); } fn get_data_dir() -> PathBuf { let mut data_dir = dirs::data_dir().expect("Couldn't find user data directory"); data_dir.push("ruin/lull"); if !data_dir.exists() { std::fs::create_dir_all(&data_dir).expect("Couldn't create lull data directory"); } data_dir } fn build_ui(application: >k::Application) { let window = gtk::ApplicationWindow::new(application); window.set_title("lull"); window.set_border_width(SPACING as u32); window.set_position(gtk::WindowPosition::Center); window.set_default_size(256, 128); let button_manage_sounds = gtk::Button::with_label("manage sounds"); button_manage_sounds.connect_clicked(|_| { let mut file_manager = Command::new(file_manager()); file_manager.arg(get_data_dir()); file_manager.output().unwrap(); }); let sounds_manage = gtk::Box::new(Orientation::Vertical, SPACING); let columns = gtk::Box::new(Orientation::Horizontal, SPACING); let column_labels = gtk::Box::new(Orientation::Vertical, SPACING); let column_sliders = gtk::Box::new(Orientation::Vertical, SPACING); columns.set_homogeneous(false); column_labels.set_homogeneous(true); column_sliders.set_homogeneous(true); column_labels.set_property_expand(false); column_sliders.set_property_expand(true); column_sliders.set_property_width_request(128); window.add(&sounds_manage); sounds_manage.add(&columns); sounds_manage.add(&button_manage_sounds); columns.add(&column_labels); columns.add(&column_sliders); let device = rodio::default_output_device().unwrap(); let mut paths = std::fs::read_dir(get_data_dir()) .expect("Couldn't read lull sounds directory") .map(|res| res.map(|e| e.path())) .collect::, std::io::Error>>() .expect("Couldn't read files from lull sounds directory"); paths.sort(); for path in paths { let name: &str = path.file_stem().unwrap().to_str().unwrap(); let file = File::open(&path) .expect("Couldn't open audio file"); let source = rodio::Decoder::new( BufReader::new(file) ); if source.is_err() { error_popup(&format!( "Couldn't parse file {}. \n{}.", path.to_str().unwrap(), source.err().unwrap() )); continue; } let source = source.unwrap().repeat_infinite(); let sink = Sink::new(&device); sink.append(source); sink.pause(); let label = gtk::Label::new(Some(name)); label.set_halign(gtk::Align::End); column_labels.add(&label); let adjustment = gtk::Adjustment::new( 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 ); let slider = gtk::Scale::new( Orientation::Horizontal, Some(&adjustment) ); slider.set_draw_value(false); slider.connect_value_changed(move |scale| { let volume = scale.get_value(); if volume == 0. { sink.pause(); } else { sink.play(); sink.set_volume(volume as f32); } }); column_sliders.add(&slider); } window.show_all(); } fn main() { let application = gtk::Application::new( Some("dev.tinybird.max.lull"), Default::default() ).expect("Initialization failed..."); application.connect_activate(|app| { build_ui(app); }); application.run(&args().collect::>()); }