#pragma once #include "Library.hpp" #include "Player.hpp" #include "Containers.hpp" #include "Control.hpp" #include "Docking.hpp" #include "MediaControls.hpp" #include "Menus.hpp" #include "TextField.hpp" #include "Toggles.hpp" #include #include #include namespace tp { class TrackWidget : public ui::Control { public: explicit TrackWidget(const Track& track); [[nodiscard]] const Track& track() const { return mTrack; } void setSelected(bool selected); void setPlaying(bool playing); [[nodiscard]] bool isPlaying() const { return mPlaying; } protected: [[nodiscard]] ui::Extent measureContent(ui::Axis axis, halnf crossLength) const override; void paint(ui::Painter& painter) override; [[nodiscard]] RGBA backgroundTarget() const override; private: const Track& mTrack; bool mSelected = false; bool mPlaying = false; }; enum class ExistenceFilter { All, Existing, Missing }; struct TrackFilter { std::string text; bool lovedOnly = false; ExistenceFilter existence = ExistenceFilter::All; [[nodiscard]] bool accepts(const Track& track) const; }; class PlaybackDevice { public: virtual ~PlaybackDevice() = default; virtual bool start(SongId id) = 0; [[nodiscard]] virtual SongId playingId() const = 0; virtual void resume() = 0; virtual void pause() = 0; [[nodiscard]] virtual halnf progress() const = 0; virtual void setProgress(halnf progress) = 0; [[nodiscard]] virtual halnf loaded() const = 0; virtual void setVolume(halnf volume) = 0; }; class PlayerDevice : public PlaybackDevice { public: explicit PlayerDevice(Player& player) : mPlayer(player) {} bool start(SongId id) override { return mPlayer.startStreamTrack(id); } [[nodiscard]] SongId playingId() const override { return mPlayer.getPlayingId(); } void resume() override { mPlayer.continuePlayback(); } void pause() override { mPlayer.freezePlayback(); } [[nodiscard]] halnf progress() const override { return mPlayer.getPlaybackProgress(); } void setProgress(halnf progress) override { mPlayer.setPlaybackProgress(progress); } [[nodiscard]] halnf loaded() const override { return mPlayer.getLoadProgress(); } void setVolume(halnf volume) override { mPlayer.setVolume(volume); } private: Player& mPlayer; }; class TrackInfoView : public ui::Widget { public: TrackInfoView(); void showTrack(const Track* track, bool playing); [[nodiscard]] const Track* track() const { return mTrack; } ui::Panel cover{ ui::Panel::Surface::Raised }; ui::Label state; ui::Label title; ui::Label artist; ui::Label details; private: const Track* mTrack = nullptr; ui::ScrollView mScroll; ui::Widget mColumn; }; class LibraryWidget : public ui::Panel { public: LibraryWidget(Library& library, PlaybackDevice& device); void rebuildTracks(); void applyFilter(); void buildMenus(ui::MenuBar& menuBar); void play(const Track& track); void togglePlaying(); void playAdjacent(int direction); [[nodiscard]] const Track* selectedTrack() const { return mSelected; } [[nodiscard]] const Track* playingTrack() const { return mPlaying; } [[nodiscard]] const std::vector& visibleTracks() const { return mVisible; } [[nodiscard]] ui::ScrollView& scroll() { return mScroll; } ui::TextField search; ui::Checkbox lovedOnly{ "Loved only" }; ui::Dropdown files{ { "All files", "Existing", "Missing" } }; ui::Label count; ui::DockArea dock; ui::DockPanel infoPanel{ "Info", ui::DockSide::Right }; TrackInfoView info; ui::PlayerBar player; ui::Menu fileMenu; ui::Menu viewMenu; ui::Menu playbackMenu; protected: void onEvent(ui::Event& event) override; void onTick(halnf deltaMs) override; private: void select(TrackWidget& widget); void refreshPlayingMarks(); void refreshInfo(); Library& mLibrary; PlaybackDevice& mDevice; ui::Panel mFilterBar{ ui::Panel::Surface::None }; ui::Widget mBrowser; ui::ScrollView mScroll; ui::Widget mTrackList; std::vector> mTracks; std::vector mVisible; ui::NowPlaying mNowPlaying; const Track* mSelected = nullptr; const Track* mPlaying = nullptr; bool mPaused = true; halnf mVolume = 1; TrackFilter mFilter; }; }