daemos 0.1.1

A local-only music player
Documentation
pub mod menu_bar;
pub mod modals;
pub mod playback;
pub mod popups;
pub mod tables;
pub mod utils;

use std::{fmt, rc::Rc};

use crossbeam::channel::Sender;
use egui_dock::{DockState, NodeIndex, TabViewer};
use popups::settings::SettingsPopup;
use tables::{playlists::PlaylistTable, tags::TagTable, tasks::TaskTable, tracks::TrackTable};

use crate::{
    components::{
        menu_bar::MenuBar, modals::create_playlist::CreatePlaylistModal, playback::PlaybackBar,
        popups::debug::performance::PerformanceMetricsPopup,
    },
    config::core::SharedConfig,
    context::SharedContext,
    database::connection::DatabaseCommand,
    playback::state::PlayerCommand,
};

#[derive(Debug, Clone)]
pub enum ComponentTab {
    Playlists,
    Tracks,
    Tags,
    Tasks,
}

impl fmt::Display for ComponentTab {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let label = match self {
            ComponentTab::Playlists => "Playlists",
            ComponentTab::Tracks => "Tracks",
            ComponentTab::Tags => "Tags",
            ComponentTab::Tasks => "Tasks",
        };

        write!(f, "{label}")
    }
}

#[derive(Debug, Clone)]
pub struct ComponentChannels {
    database_command_tx: Sender<DatabaseCommand>,
    player_command_tx: Sender<PlayerCommand>,
}

impl ComponentChannels {
    pub fn new(
        database_command_tx: Sender<DatabaseCommand>,
        player_command_tx: Sender<PlayerCommand>,
    ) -> Self {
        Self {
            database_command_tx,
            player_command_tx,
        }
    }
}

pub struct Components {
    pub top_menu_bar: MenuBar,
    pub playback_bar: PlaybackBar,

    pub playlist_table: PlaylistTable,
    pub track_table: TrackTable,
    pub tag_table: TagTable,
    pub task_table: TaskTable,

    pub settings: SettingsPopup,
    pub debug: PerformanceMetricsPopup,
    pub create_playlist: CreatePlaylistModal,
}

impl Components {
    pub fn new(
        config: SharedConfig,
        context: SharedContext,
        channels: Rc<ComponentChannels>,
    ) -> Self {
        Self {
            top_menu_bar: MenuBar::new(context.clone()),
            playback_bar: PlaybackBar::new(config.clone(), context.clone(), channels.clone()),

            playlist_table: PlaylistTable::new(context.clone(), channels.clone()),
            track_table: TrackTable::new(config.clone(), context.clone(), channels.clone()),
            tag_table: TagTable::default(),
            task_table: TaskTable::default(),

            settings: SettingsPopup::new(config.clone(), context.clone()),
            debug: PerformanceMetricsPopup::new(config.clone(), context.clone()),
            create_playlist: CreatePlaylistModal::new(context.clone(), channels.clone()),
        }
    }

    pub fn component_tab_layout(&self) -> DockState<ComponentTab> {
        let mut dock_state = DockState::new(vec![
            ComponentTab::Tracks,
            ComponentTab::Tags,
            ComponentTab::Tasks,
        ]);

        let surface = dock_state.main_surface_mut();

        let [_, _] = surface.split_left(NodeIndex::root(), 0.20, vec![ComponentTab::Playlists]);

        dock_state
    }
}

impl TabViewer for Components {
    type Tab = ComponentTab;

    fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
        tab.to_string().into()
    }

    fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
        match tab {
            ComponentTab::Playlists => {
                self.playlist_table.ui(ui);
            }
            ComponentTab::Tracks => {
                self.track_table.ui(ui);
            }
            ComponentTab::Tags => {
                self.tag_table.ui(ui);
            }
            ComponentTab::Tasks => {
                self.task_table.ui(ui);
            }
        }
    }
}