The library viewer is laid out in a dock area: the filter bar and track list in the centre and an Info panel docked on the right, which can be hidden, unpinned or floated like any dock panel and toggled from View. It shows a cover art placeholder, whether the track is selected or now playing, the title and artist, and album, genre, year, length, plays, loved, file state and date added on separate lines.
157 lines
4.2 KiB
C++
157 lines
4.2 KiB
C++
#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 <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<TrackWidget*>& 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<std::unique_ptr<TrackWidget>> mTracks;
|
|
std::vector<TrackWidget*> mVisible;
|
|
ui::NowPlaying mNowPlaying;
|
|
|
|
const Track* mSelected = nullptr;
|
|
const Track* mPlaying = nullptr;
|
|
bool mPaused = true;
|
|
halnf mVolume = 1;
|
|
TrackFilter mFilter;
|
|
};
|
|
}
|