hum-0.1.0.0: src/Hum/Views/Playlists.hs
{-#LANGUAGE RankNTypes#-}
-- |
module Hum.Views.Playlists where
import Hum.Types
import Brick.Types
import Graphics.Vty.Input.Events
import Brick.Main
import Brick.Widgets.Core
import Brick.Widgets.Center
import Brick.Widgets.Border
import Brick.Widgets.Border.Style
import Control.Lens
import Brick.Widgets.List
import Hum.Attributes
import Hum.Views.Common
import Hum.Rebuild
import qualified Data.Text as T
import Network.MPD ( withMPD )
import qualified Network.MPD as MPD
import qualified Data.Map.Strict as Map
import Hum.Utils
drawPlaylistLeft :: HState -> Widget Name
drawPlaylistLeft st =
let vsize = case join $ Map.lookup PlaylistLeft $ extentMap st of
Just e -> snd . extentSize $ e
Nothing -> 20
in reportExtent PlaylistLeft $ hCenter
( viewport PlaylistLeft Vertical
. visible
. vLimit vsize
. center
$ hBorder
<=> hCenter
(renderList (const $ playlistRow st)
((focPlay . focus $ st) == FocPlaylists)
(MPD.toText <$> st ^. playlistsL . plListL)
)
)
drawPlaylistRight :: HState -> Widget Name
drawPlaylistRight st =
let vsize = case join $ Map.lookup PlaylistRight $ extentMap st of
Just e -> snd . extentSize $ e
Nothing -> 20
in reportExtent PlaylistRight $ hCenter
( viewport PlaylistRight Vertical
. visible
. vLimit vsize
. center
$ (if st ^. editableL
then
withDefAttr listHighlightedAttr
. withBorderStyle (borderStyleFromChar '=')
$ hBorderWithLabel
$ txt "editing"
else hBorder
)
<=> hCenter
(renderList (const $ playlistSongRow st)
((focPlay . focus $ st) == FocPSongs)
(st ^. playlistsL . plSongsL)
)
)
playlistRow :: HState -> T.Text -> Widget n
playlistRow _ val =
withAttr queueAlbumAttr $ column Nothing (Pad 1) Max $ txt val
playlistSongRow :: HState -> (MPD.Song,Highlight) -> Widget n
playlistSongRow st (song,hl) =
let pathsInQueue =
(MPD.sgFilePath <$>) . (fst <$>) . listElements . queue $ st
in (if hl then highlightOverQueueAttrs else id) . withAttr
(if MPD.sgFilePath song `elem` pathsInQueue
then queueTitleBoldAttr
else queueTitleAttr
)
$ column Nothing (Pad 1) Max
$ txt (meta (MPD.toText . MPD.sgFilePath $ song) MPD.Title song)
drawViewPlaylists :: HState -> Widget Name
drawViewPlaylists st =
hLimitPercent 25 (drawPlaylistLeft st) <+> drawPlaylistRight st
playlistsMove
:: (forall e . List Name e -> List Name e) -> HState -> EventM Name HState
playlistsMove moveFunc s =
let playfoc = s ^. focusL . focPlayL
in case playfoc of
FocPlaylists -> rebuildPlList $ s & playlistsL . plListL %~ moveFunc
FocPSongs -> do
pure $ s & playlistsL . plSongsL %~ moveFunc
playlistsAddtoQ :: Bool -> HState -> EventM Name HState
playlistsAddtoQ play s =
let playfoc = s ^. focusL . focPlayL
in case playfoc of
FocPlaylists -> do
songBulkAddtoQ play (fst <$> listElements (s ^. playlistsL . plSongsL)) s
FocPSongs -> do
let maybeFilePath = MPD.sgFilePath . fst . snd <$> listSelectedElement
(s ^. playlistsL . plSongsL)
traverse_
(\sel -> liftIO
(withMPD $ MPD.addId sel Nothing >>= if play
then MPD.playId
else const pass
)
)
maybeFilePath
song <- liftIO (withMPD MPD.currentSong)
pure s { currentSong = fromRight Nothing song, queue = queue s }
playlistsSearch :: Bool -> HState -> EventM Name HState
playlistsSearch direction s =
let playfoc = s ^. focusL . focPlayL
dir = if direction then id else listReverse
searchkey = fromMaybe "" ((s ^. exL . searchHistoryL) !!? 0)
in if searchkey == ""
then pure s
else case playfoc of
FocPlaylists -> do
extentMap <- updateExtentMap
rebuildPlList
$ (s { extentMap })
& playlistsL
. plListL
%~ (dir . listFindBy (stringySearch searchkey) . dir)
FocPSongs -> do
extentMap <- updateExtentMap
pure
$ s { extentMap }
& playlistsL
. plSongsL
%~ (dir . listFindBy (songSearch searchkey [MPD.Title] . fst) . dir)
handleEventPlaylists
:: HState -> BrickEvent Name HumEvent -> EventM Name (Next HState)
handleEventPlaylists s e = case e of
VtyEvent vtye -> case vtye of
EvKey (KChar 'j') [] -> continue =<< playlistsMove listMoveDown s
EvKey (KChar 'k') [] -> continue =<< playlistsMove listMoveUp s
EvKey (KChar 'l') [] -> continue $ s & focusL . focPlayL .~ FocPSongs
EvKey (KChar 'h') [] -> continue $ s & focusL . focPlayL .~ FocPlaylists
EvKey (KChar 'n') [] ->
continue =<< playlistsSearch (s ^. exL . searchDirectionL) s
EvKey (KChar 'N') [] ->
continue =<< playlistsSearch (s ^. exL . searchDirectionL & not) s
EvKey KEnter [] ->
continue =<< playlistsMove listMoveDown =<< playlistsAddtoQ True s
EvKey (KChar ' ') [] -> if s ^. editableL then
continue $ s & playlistsL . plSongsL %~ (listMoveDown . listToggleHighlight)
else
continue =<< playlistsMove listMoveDown =<< playlistsAddtoQ False s
EvKey (KChar 'd') []
| s ^. editableL
-> continue $ deleteHighlighted s (playlistsL . plSongsL)
| s ^. focusL . focPlayL == FocPlaylists
-> continue $ s & modeL .~ PromptMode
& promptsL . currentPromptL .~ YNPrompt
& promptsL . promptTitleL .~ ("DELETE " <> selectedPl <> "?\nYou can't paste it back yet")
& promptsL . exitPromptL .~ deleteSelectedPl
| otherwise -> continue s
EvKey (KChar 'y') []
| s ^. editableL
-> continue $ s & clipboardL . clSongsL .~ (s ^. playlistsL . plSongsL & getHighlighted)
| s ^. focusL . focPlayL == FocPlaylists
-> continue $ s & clipboardL . clPlNameL .~ (s ^. playlistsL . plListL & listSelectedElement <&> snd)
| otherwise -> continue s
EvKey (KChar 'p') []
| s ^. editableL
-> continue $ s & playlistsL . plSongsL %~ listPaste (s^. clipboardL. clSongsL)
| s ^. focusL . focPlayL == FocPlaylists
-> continue =<< pastePlaylist s
| otherwise -> continue s
EvKey (KChar 'G') [] ->
continue =<< playlistsMove (\ls -> listMoveBy (length ls) ls) s
EvKey (KChar 'g') [] -> continue =<< playlistsMove (listMoveTo 0) s -- TODO change this to 'gg', somehow
EvKey (KChar 'e') [] -> if s ^. editableL then
continue $ s & editableL %~ not
& modeL .~ PromptMode
& promptsL . currentPromptL .~ YNPrompt
& promptsL . promptTitleL .~ ("Save changes to " <> selectedPl <> "?")
& promptsL . exitPromptL .~ saveEditedPl
else continue =<< rebuildPlList (s & editableL %~ not)
_ -> continue s
_ -> continue s
where selectedPl = s ^. playlistsL . plListL & listSelectedElement ? maybe "<error>" snd ? MPD.toText