A tool to sync music with your favorite devices
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

*: remove gpui gui, all-in on gtk4

Gee Sawra 231abaa0 f95b6a84

+3 -351
+2
src/gtkgui/mod.rs
··· 1 1 mod gtkgui; 2 + 3 + #[cfg(feature = "gui")] 2 4 pub use gtkgui::run;
-161
src/gui/gui.rs
··· 1 - use crate::{ 2 - db, 3 - gui::{sync::SyncView, track_table::TrackTableDelegate}, 4 - model, 5 - }; 6 - use anyhow::{Context as AnyhowContext, Result}; 7 - use gpui::{prelude::FluentBuilder, *}; 8 - use gpui_component::{ 9 - dialog::Dialog, 10 - menu::AppMenuBar, 11 - popover::Popover, 12 - table::{Table, TableState}, 13 - *, 14 - }; 15 - 16 - actions!([Sync, Quit]); 17 - 18 - pub async fn run() -> Result<()> { 19 - let db = db::Instance::new(db::default_database_dir().to_str().unwrap(), false) 20 - .await 21 - .with_context(|| "Cannot open local database instance")?; 22 - 23 - let tracks = db.tracks_by_state(model::FileState::Copied).await?; 24 - 25 - let app = Application::new().with_assets(gpui_component_assets::Assets); 26 - 27 - app.run(move |cx| { 28 - gpui_component::init(cx); 29 - 30 - cx.on_action(|_: &Quit, cx: &mut App| { 31 - cx.quit(); 32 - }); 33 - 34 - cx.set_menus(vec![Menu { 35 - name: "File".into(), 36 - items: vec![ 37 - MenuItem::action("Sync", Sync), 38 - MenuItem::Separator, 39 - MenuItem::action("Quit", Quit), 40 - ], 41 - }]); 42 - 43 - let mb = AppMenuBar::new(cx); 44 - 45 - let window_options = WindowOptions { 46 - titlebar: Some(TitleBar::title_bar_options()), 47 - window_bounds: Some(WindowBounds::centered(size(px(680.), px(600.)), cx)), 48 - ..Default::default() 49 - }; 50 - 51 - cx.spawn(async move |cx| { 52 - cx.open_window(window_options, |window, cx| { 53 - window.activate_window(); 54 - window.set_window_title("tunz"); 55 - 56 - Theme::change(ThemeMode::Dark, Some(window), cx); 57 - 58 - let view = cx.new(|cx| Tunz::new(window, cx, tracks, mb)); 59 - 60 - // This first level on the window, should be a Root. 61 - cx.new(|cx| Root::new(view, window, cx)) 62 - })?; 63 - 64 - Ok::<_, anyhow::Error>(()) 65 - }) 66 - .detach(); 67 - }); 68 - Ok(()) 69 - } 70 - 71 - pub struct Tunz { 72 - tracks_table: Entity<TableState<TrackTableDelegate>>, 73 - menu_bar: Entity<AppMenuBar>, 74 - show_sync: bool, 75 - } 76 - 77 - impl Tunz { 78 - fn new( 79 - window: &mut Window, 80 - cx: &mut Context<Self>, 81 - tracks: Vec<model::Track>, 82 - mb: Entity<AppMenuBar>, 83 - ) -> Self { 84 - let td = TrackTableDelegate::new(tracks); 85 - let td = cx.new(|cx| { 86 - TableState::new(td, window, cx) 87 - .col_selectable(false) 88 - .col_movable(false) 89 - }); 90 - 91 - Self { 92 - tracks_table: td, 93 - menu_bar: mb, 94 - show_sync: false, 95 - } 96 - } 97 - } 98 - 99 - impl Render for Tunz { 100 - fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { 101 - div() 102 - .size_full() 103 - .on_action(cx.listener(|this, _: &Sync, window, cx| { 104 - this.show_sync = true; 105 - window.open_dialog(cx, |d, _, _| d.title("test")); 106 - log::info!("sync pressed!"); 107 - })) 108 - .size_full() 109 - .child( 110 - TitleBar::new().child( 111 - div() 112 - .flex() 113 - .w_full() 114 - .items_center() 115 - .justify_between() 116 - .child(self.menu_bar.clone()) 117 - // Spacer for window controls 118 - .child(div().w_6()), 119 - ), 120 - ) 121 - .bg(cx.theme().background) 122 - // .child(self.render_menu(cx)) 123 - .child(self.render_track_list(cx)) 124 - .child(self.render_status_bar(cx)) 125 - } 126 - } 127 - 128 - impl Tunz { 129 - fn render_track_list(&mut self, cx: &mut Context<Self>) -> impl IntoElement { 130 - v_flex().size_full().child(Table::new(&self.tracks_table)) 131 - } 132 - 133 - fn render_status_bar(&self, cx: &Context<Self>) -> impl IntoElement { 134 - let tracks = self.tracks_table.read(cx); 135 - 136 - h_flex() 137 - .px_3() 138 - .gap_4() 139 - .h_7() 140 - .text_sm() 141 - .items_center() 142 - .justify_between() 143 - .border_t_1() 144 - .border_color(cx.theme().border) 145 - .bg(cx.theme().tab_bar) 146 - .text_color(cx.theme().muted_foreground) 147 - .child( 148 - h_flex() 149 - .gap_4() 150 - // Memory info 151 - .child({ 152 - h_flex() 153 - .gap_2() 154 - .w(px(135.)) 155 - .items_center() 156 - .child(Icon::new(IconName::HardDrive)) 157 - .child(format!("{} tracks", tracks.delegate().tracks_amount())) 158 - }), 159 - ) 160 - } 161 - }
-8
src/gui/mod.rs
··· 1 - #[cfg(feature = "gui")] 2 - mod gui; 3 - #[cfg(feature = "gui")] 4 - pub use gui::run; 5 - #[cfg(feature = "gui")] 6 - mod sync; 7 - #[cfg(feature = "gui")] 8 - mod track_table;
-15
src/gui/sync.rs
··· 1 - use std::any::TypeId; 2 - 3 - use gpui::{Context, InteractiveElement, IntoElement, Render, Styled, Window, actions, div, rgb}; 4 - use gpui_component::dialog::Dialog; 5 - 6 - pub struct SyncView {} 7 - impl Render for SyncView { 8 - fn render( 9 - &mut self, 10 - window: &mut gpui::Window, 11 - cx: &mut gpui::Context<Self>, 12 - ) -> impl gpui::IntoElement { 13 - Dialog::new(window, cx) 14 - } 15 - }
-159
src/gui/track_table.rs
··· 1 - use gpui::{Context, IntoElement, ParentElement, Styled, Window, div}; 2 - use gpui_component::{ 3 - ActiveTheme, StyledExt, 4 - table::{Column, ColumnSort, TableDelegate, TableState}, 5 - }; 6 - 7 - use crate::model; 8 - 9 - enum TrackSort { 10 - Title, 11 - Album, 12 - Artist, 13 - Format, 14 - } 15 - 16 - pub struct TrackTableDelegate { 17 - tracks: Vec<model::Track>, 18 - columns: Vec<Column>, 19 - sort_type: TrackSort, 20 - sort_order: ColumnSort, 21 - } 22 - 23 - impl TrackTableDelegate { 24 - pub fn new(tracks: Vec<model::Track>) -> Self { 25 - let mut s = Self { 26 - tracks: tracks, 27 - columns: vec![ 28 - Column::new("title", "Title") 29 - .min_width(500.) 30 - .max_width(800.) 31 - .sortable(), 32 - Column::new("artist", "Artist") 33 - .min_width(200.) 34 - .max_width(500.) 35 - .sortable(), 36 - Column::new("album", "Album") 37 - .min_width(300.) 38 - .max_width(500.) 39 - .sortable() 40 - .sortable(), 41 - Column::new("format", "Format") 42 - .min_width(60.) 43 - .max_width(500.) 44 - .sortable() 45 - .sortable(), 46 - ], 47 - sort_type: TrackSort::Title, 48 - sort_order: ColumnSort::Ascending, 49 - }; 50 - 51 - s.sort(); 52 - 53 - s 54 - } 55 - 56 - pub fn tracks_amount(&self) -> usize { 57 - self.tracks.len() 58 - } 59 - 60 - fn sort(&mut self) { 61 - let is_descending = matches!(self.sort_order, ColumnSort::Descending); 62 - 63 - match self.sort_type { 64 - TrackSort::Title => { 65 - self.tracks.sort_by(|a, b| { 66 - let cmp = a.title.to_lowercase().cmp(&b.title.to_lowercase()); 67 - if is_descending { cmp.reverse() } else { cmp } 68 - }); 69 - } 70 - TrackSort::Album => { 71 - self.tracks.sort_by(|a, b| { 72 - let cmp = a.album.to_lowercase().cmp(&b.album.to_lowercase()); 73 - if is_descending { cmp.reverse() } else { cmp } 74 - }); 75 - } 76 - TrackSort::Artist => { 77 - self.tracks.sort_by(|a, b| { 78 - let cmp = a.artist.to_lowercase().cmp(&b.artist.to_lowercase()); 79 - if is_descending { cmp.reverse() } else { cmp } 80 - }); 81 - } 82 - TrackSort::Format => { 83 - self.tracks.sort_by(|a, b| { 84 - let cmp = a.extension.cmp(&b.extension); 85 - if is_descending { cmp.reverse() } else { cmp } 86 - }); 87 - } 88 - } 89 - } 90 - } 91 - 92 - impl TableDelegate for TrackTableDelegate { 93 - fn columns_count(&self, cx: &gpui::App) -> usize { 94 - self.columns.len() 95 - } 96 - 97 - fn rows_count(&self, cx: &gpui::App) -> usize { 98 - self.tracks.len() 99 - } 100 - 101 - fn column(&self, col_ix: usize, cx: &gpui::App) -> gpui_component::table::Column { 102 - self.columns[col_ix].clone() 103 - } 104 - 105 - fn render_td( 106 - &mut self, 107 - row_ix: usize, 108 - col_ix: usize, 109 - window: &mut gpui::Window, 110 - cx: &mut gpui::Context<gpui_component::table::TableState<Self>>, 111 - ) -> impl gpui::IntoElement { 112 - let Some(track) = self.tracks.get(row_ix) else { 113 - return div().into_any_element(); 114 - }; 115 - 116 - match col_ix { 117 - 0 => div() 118 - .text_xs() 119 - .text_color(cx.theme().accent_foreground) 120 - .child(track.title.clone()) 121 - .into_any_element(), 122 - 1 => div() 123 - .text_sm() 124 - .text_color(cx.theme().foreground) 125 - .truncate() 126 - .child(track.artist.clone()) 127 - .into_any_element(), 128 - 2 => div() 129 - .text_xs() 130 - .child(track.album.clone()) 131 - .into_any_element(), 132 - 3 => div() 133 - .text_xs() 134 - .font_bold() 135 - .text_color(cx.theme().muted_foreground) 136 - .child(track.extension.clone().to_uppercase()) 137 - .into_any_element(), 138 - _ => div().into_any_element(), 139 - } 140 - } 141 - 142 - fn perform_sort( 143 - &mut self, 144 - col_ix: usize, 145 - sort: ColumnSort, 146 - _: &mut Window, 147 - _: &mut Context<TableState<Self>>, 148 - ) { 149 - self.sort_order = sort; 150 - self.sort_type = match col_ix { 151 - 0 => TrackSort::Title, 152 - 1 => TrackSort::Album, 153 - 2 => TrackSort::Artist, 154 - 3 => TrackSort::Format, 155 - _ => TrackSort::Title, 156 - }; 157 - self.sort(); 158 - } 159 - }
+1 -8
src/main.rs
··· 7 7 mod fs; 8 8 mod gpod; 9 9 mod gtkgui; 10 - mod gui; 11 10 mod library; 12 11 mod model; 13 12 mod sync; ··· 16 15 async fn main() -> anyhow::Result<()> { 17 16 log_setup(); 18 17 19 - // One day I may get back to this. 20 - // Today is not one of those days. 21 18 #[cfg(feature = "gui")] 22 19 { 23 20 if std::env::args().count() == 1 { 24 - return gui::run().await; 21 + return gtkgui::run().await; 25 22 } 26 - } 27 - 28 - if std::env::args().count() == 1 { 29 - return gtkgui::run().await; 30 23 } 31 24 32 25 let c = cli::Cli::parse();