Refactor lib view

This commit is contained in:
IlyaShurupov 2024-01-31 21:21:23 +03:00
parent 83d51d3e83
commit 3b50001daf
9 changed files with 270 additions and 427 deletions

View file

@ -2,7 +2,7 @@ project(LibraryViewer)
### ---------------------- Externals --------------------- ### ### ---------------------- Externals --------------------- ###
set(BINDINGS_INCLUDE ../Externals/glfw/include ${GLEW_INCLUDE_DIR} ${PORTAUDIO_INCLUDE_DIR}) set(BINDINGS_INCLUDE ../Externals/glfw/include ${GLEW_INCLUDE_DIR} ${PORTAUDIO_INCLUDE_DIR})
set(BINDINGS_LIBS glfw ${GLEW_LIB} ${ALSA_LIBRARY} ${PORTAUDIO_LIB}) set(BINDINGS_LIBS glfw Imgui ${GLEW_LIB} ${ALSA_LIBRARY} ${PORTAUDIO_LIB})
### ---------------------- Static Library --------------------- ### ### ---------------------- Static Library --------------------- ###
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp") file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
@ -14,7 +14,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC ./public/ ${BINDINGS_INCLUDE}
target_link_libraries(${PROJECT_NAME} PUBLIC Graphics Connection Widgets) target_link_libraries(${PROJECT_NAME} PUBLIC Graphics Connection Widgets)
target_link_libraries(${PROJECT_NAME} PUBLIC ${BINDINGS_LIBS}) target_link_libraries(${PROJECT_NAME} PUBLIC ${BINDINGS_LIBS})
file(COPY "library" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") file(COPY "library" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}")
file(COPY "applications/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") file(COPY "applications/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
### -------------------------- Applications -------------------------- ### ### -------------------------- Applications -------------------------- ###

View file

@ -3,22 +3,27 @@
#include "Player.hpp" #include "Player.hpp"
// 1) artworks // 1) artworks
// 2) how to easy add more songs? // 2) how to easily add more songs?
// 3) GUi : // 3) GUi :
// - seeker // - seeker
// - song idx // - song idx
// - non existing highlight // - non-existing highlight
// - prev next // - prev next
// - remove debug gui // - remove debug gui
// 4) queue & repeat & shuffle... // 4) queue & repeat & shuffle...
// 5) new database with history // 5) new database with history
void runApp() { void runApp() {
Player player; Player player;
Library library; Library library;
library.loadJson(getHome() + "Library.json"); library.loadJson(getHome() + "Library.json");
library.checkExisting(); library.checkExisting();
LibraryWidget<tp::Window::Events, tp::Graphics::Canvas> gui(&library, &player);
tp::GlobalGUIConfig config;
tp::gGlobalGUIConfig = &config;
tp::LibraryWidget<tp::Window::Events, tp::Graphics::Canvas> gui(&library, &player);
auto window = tp::Window::createWindow(800, 600, "Window 1"); auto window = tp::Window::createWindow(800, 600, "Window 1");
@ -29,7 +34,7 @@ void runApp() {
auto area = window->getCanvas().getAvaliableArea(); auto area = window->getCanvas().getAvaliableArea();
gui.proc(window->getEvents(), {}, { area.x, area.y, area.z, area.w }); gui.proc(window->getEvents(), {}, { area.x, area.y, area.z, area.w });
gui.draw(window->getCanvas(), {}, { area.x, area.y, area.z, area.w }); gui.draw(window->getCanvas());
// tp::sleep(100); // tp::sleep(100);

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,12 +1,13 @@
#include "Library.hpp" #include "Library.hpp"
#include "LocalConnection.hpp" #include "LocalConnection.hpp"
#include "WidgetBase.hpp"
#include "picojson.h" #include "picojson.h"
#include <filesystem> #include <filesystem>
namespace tp { namespace tp {
static ModuleManifest* deps[] = { &gModuleGraphics, &gModuleConnection, nullptr }; static ModuleManifest* deps[] = { &gModuleGraphics, &gModuleConnection, &gModuleWidgets, nullptr };
ModuleManifest gModuleLibraryViewer = ModuleManifest("LibraryViewer", nullptr, nullptr, deps); ModuleManifest gModuleLibraryViewer = ModuleManifest("LibraryViewer", nullptr, nullptr, deps);
} }
@ -23,15 +24,13 @@ tp::String getHome() {
if (envVarValue != nullptr) { if (envVarValue != nullptr) {
tp::String out; tp::String out;
out = (int1*) (std::string(envVarValue) + "/").c_str(); out = (int1*) (std::string(envVarValue) + "/").c_str();
return out; return out;
} else { } else {
return ""; return "library/";
} }
} }
tp::String getSongLocalPath(SongId id) { tp::String getSongLocalPath(SongId id) { return getHome() + "tracks/" + tp::String((tp::alni) id); }
return getHome() + "tracks/" + tp::String((tp::alni)id);
}
SONG_FORMAT getSongFormat(const String& localPath) { SONG_FORMAT getSongFormat(const String& localPath) {
std::filesystem::path wavFormat((localPath + ".wav").read()); std::filesystem::path wavFormat((localPath + ".wav").read());
@ -56,21 +55,24 @@ bool Library::loadJson(const String& path) {
libraryFileMem.reserve(libraryFile.size()); libraryFileMem.reserve(libraryFile.size());
libraryFile.readBytes(libraryFileMem.getBuff(), libraryFile.size()); libraryFile.readBytes(libraryFileMem.getBuff(), libraryFile.size());
std::string json = libraryFileMem.getBuff(); std::string json = libraryFileMem.getBuff();
picojson::value jsonNode; picojson::value jsonNode;
std::string err = picojson::parse(jsonNode, json); std::string err = picojson::parse(jsonNode, json);
if (! err.empty()) { if (!err.empty()) {
printf("Given path - %s\n", path.read());
printf("Home for library is - '%s'. Set it in the env as LIB_VIEW_HOME \n", getHome().read());
printf("Error parsing json library file - check your paths and files\n %s \n", err.c_str());
return false; return false;
} }
auto & traks = jsonNode.get<picojson::array>(); auto& traks = jsonNode.get<picojson::array>();
for (auto & trackNode : traks) { for (auto& trackNode : traks) {
Track newTrack; Track newTrack;
auto & track = trackNode.get<picojson::object>(); auto& track = trackNode.get<picojson::object>();
for (auto & trackProperty : track) { for (auto& trackProperty : track) {
if (PROP(Name)) newTrack.mName = AS_STR; if (PROP(Name)) newTrack.mName = AS_STR;
else if (PROP(Artist)) newTrack.mArtist = AS_STR; else if (PROP(Artist)) newTrack.mArtist = AS_STR;
else if (PROP(Track ID)) newTrack.mId = AS_INT; else if (PROP(Track ID)) newTrack.mId = AS_INT;

View file

@ -4,459 +4,295 @@
#include "Player.hpp" #include "Player.hpp"
#include "Widgets.hpp" #include "Widgets.hpp"
template <typename Events, typename Canvas> #include "imgui.h"
class ResizerWidget {
public:
ResizerWidget() = default;
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) { namespace tp {
if (!areaParent.isOverlap(area)) {
resizing = false;
return;
}
hover = area.isInside(events.getPos()); template <typename Events, typename Canvas>
class TrackWidget : public Widget<Events, Canvas> {
public:
explicit TrackWidget(const Track* track = nullptr) :
mTrack(track) {
this->mArea.w = 70;
col.mColor.setAnimTime(0);
col.mColor.setNoTransition({ 0.15f, 0.15f, 0.15f, 0.f });
};
if (events.isPressed() && hover) { void proc(const Events& events, const RectF& areaParent, const RectF& area) override {
resizing = true; mSelected = false;
} else if (!events.isDown()) { this->mArea = area;
resizing = false; this->mVisible = area.isOverlap(areaParent);
} if (!this->mVisible) return;
value = area.x; if (!mTrack) return;
if (!areaParent.isOverlap(area)) return;
if (resizing) { if (area.isInside(events.getPos())) {
tp::halnf pos = events.getPos().x; col.set({ 0.15f, 0.15f, 0.15f, 1.f });
auto diff = (pos - value - area.z / 2.f); mSelected = events.isReleased();
value += diff;
}
value = tp::clamp(value, min, max);
if (min > max) {
value = min;
}
}
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
if (!areaParent.isOverlap(area)) return;
if (hover || resizing) {
canvas.rect(area, { 0.23f, 0.23f, 0.23f, 1.f }, 0.f);
} else {
canvas.rect(area, { 0.04f, 0.04f, 0.04f, 1.f }, 0.f);
}
}
public:
bool hover = false;
bool resizing = false;
tp::halnf max = 0;
tp::halnf min = 0;
tp::halnf value = 0;
};
template <typename Events, typename Canvas>
requires(DrawableConcept<Canvas>)
class TrackInfoWidget {
struct SortType {
tp::String text;
bool dec = false;
bool inc = false;
};
public:
TrackInfoWidget(const Track* track = nullptr) :
mTrack(track) {
items.append({ "Date Added" });
items.append({ "Date Last Played" });
}
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) {
if (!mTrack) return;
if (!areaParent.isOverlap(area)) return;
}
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
if (!areaParent.isOverlap(area)) return;
ImGui::SetNextWindowPos({ area.x, area.y });
ImGui::SetNextWindowSize({ area.z, area.w });
RenderUI();
canvas.rect(area, { 0.13f, 0.13f, 0.13f, 1.f }, 4.f);
if (!mTrack) return;
}
void RenderUI() {
ImGui::Begin(
"InfoWindow",
0,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoResize
);
if (mTrack) {
ImGui::Text(" Load Status : %s", mLoadStatus ? "Loaded" : "Not Loaded");
if (ImGui::Button("Play")) {
if (mPlayer->getPlayingId() != mTrack->mId) {
mLoadStatus = mPlayer->startStreamTrack(mTrack->mId);
}
mPlayer->continuePlayback();
}
if (mLoadStatus) {
ImGui::SameLine();
if (ImGui::Button("Stop")) {
mPlayer->freezePlayback();
}
auto songProgress = mPlayer->getPlaybackProgress();
auto vol = mPlayer->getVolume();
ImGui::Text("Load Progress : %f", mPlayer->getLoadProgress());
if (ImGui::SliderFloat("Progress", &songProgress, 0.f, 1.f)) {
mPlayer->setPlaybackProgress(songProgress);
}
if (ImGui::SliderFloat("Volume", &vol, 0.f, 1.f)) {
mPlayer->setVolume(vol);
}
}
}
ImGui::Text("Song Info:");
if (mTrack) {
ImGui::Text("Name : %s", mTrack->mName.read());
ImGui::Text("Author : %s", mTrack->mArtist.read());
ImGui::Text("Love : %s", mTrack->mLoved ? "true" : "false");
ImGui::Text("Id : %lli", mTrack->mId);
ImGui::Text("Total Time : %lli", mTrack->mTotalTime);
ImGui::Text("Play Count : %lli", mTrack->mPlayCount);
ImGui::Text("Skip Count : %lli", mTrack->mSkipCount);
ImGui::Text("Date Added : %s", mTrack->mDateAdded.read());
ImGui::Text("Albom : %s", mTrack->mAlbum.read());
} else {
ImGui::Text("Not Selected");
}
ImGui::Separator();
if (ImGui::Checkbox("Loved only", &filterLoved)) {
isSongFilterChanged |= true;
}
if (ImGui::SliderInt("Existing only", &filterExisting, 0, 3)) {
isSongFilterChanged |= true;
}
isSongFilterChanged |= songFilter.Draw("Song Filter");
sortFilter.Draw("Sorting Type");
for (int i = 0; i < items.size(); ++i) {
if (!sortFilter.PassFilter(items[i].text.read())) continue;
ImGui::PushID(i);
if (ImGui::Button("Inc")) {
items[i].inc = true;
}
ImGui::SameLine();
if (ImGui::Button("Dec")) {
items[i].dec = true;
}
ImGui::AlignTextToFramePadding();
ImGui::SameLine();
ImGui::Text("%s", items[i].text.read());
ImGui::PopID();
}
ImGui::End();
}
public:
ImGuiTextFilter songFilter;
ImGuiTextFilter sortFilter;
tp::Buffer<SortType> items;
const Track* mTrack;
bool filterLoved = false;
Player* mPlayer = nullptr;
bool mLoadStatus = false;
bool isSongFilterChanged = true;
int filterExisting = 0; // all existing no-existing
};
template <typename Events, typename Canvas>
requires(DrawableConcept<Canvas>)
class ScrollBarWidget {
public:
ScrollBarWidget() = default;
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) {
if (mSizeFraction > 1.f) {
mPositionFraction = 0;
return;
}
if (!areaParent.isOverlap(area)) {
mIsScrolling = false;
return;
}
if (events.getScrollY() != 0 && areaParent.isInside(events.getPos())) {
auto offset = events.getScrollY() < 0 ? 1.0f : -1.0f;
if (scrollInertia * offset > 0) {
scrollInertia += offset;
} else { } else {
scrollInertia = -scrollInertia + offset; col.set({ 0.15f, 0.15f, 0.15f, 0.f });
} }
} }
if (tp::abs(scrollInertia) > 0.1f) { void draw(Canvas& canvas) const override {
auto offset = scrollInertia * mScrollFactor; if (!this->mVisible) return;
mPositionFraction += offset; if (!mTrack) return;
mPositionFraction = tp::clamp(mPositionFraction, 0.f, 1.f - mSizeFraction);
scrollInertia *= 0.f; canvas.rect(this->mArea, col.get(), 4.f);
return;
const RectF imageArea = {
this->mArea.x + margin, this->mArea.y + margin, this->mArea.w - margin * 2, this->mArea.w - margin * 2
};
canvas.rect(imageArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
const RectF textArea = { this->mArea.x + this->mArea.w + margin,
this->mArea.y + margin,
this->mArea.z - this->mArea.w - margin * 2,
this->mArea.w - margin * 2 };
// canvas.rect(textArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
const RectF textAreaName = { textArea.x, textArea.y, textArea.z, textArea.w * 0.5f };
const RectF textAreaAuthor = { textArea.x, textArea.y + textArea.w * 0.5f, textArea.z, textArea.w * 0.5f };
canvas.text(mTrack->mName.read(), textAreaName, 15.f, Canvas::LC, 4.f, { 0.9f, 0.9f, 0.9f, 1.f });
canvas.text(mTrack->mArtist.read(), textAreaAuthor, 12.f, Canvas::LC, 4.f, { 0.8f, 0.8f, 0.8f, 1.f });
} }
if (events.isPressed() && area.isInside(events.getPos())) { public:
mIsScrolling = true; halnf margin = 5.f;
} else if (!events.isDown()) { AnimColor col;
mIsScrolling = false; const Track* mTrack;
} bool mSelected = false;
if (mIsScrolling) {
tp::halnf pos = events.getPos().y;
pos = (pos - area.y - mSizeFraction * area.w / 2.f) / area.w;
mPositionFraction = tp::clamp(pos, 0.f, 1.f - mSizeFraction);
}
mPositionFraction = tp::clamp(mPositionFraction, 0.f, 1.f - mSizeFraction);
}
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
if (mSizeFraction > 1.f) return;
if (!areaParent.isOverlap(area)) return;
auto minSize = 10.f;
tp::RGBA col = tp::RGBA{ 0.25f, 0.25f, 0.25f, 1.f };
if (mIsScrolling) {
col = tp::RGBA{ 0.45f, 0.45f, 0.45f, 1.f };
minSize = 20.f;
}
canvas.rect(area, { 0.15f, 0.15f, 0.15f, 1.f }, 4.f);
auto sliderSize = tp::clamp(area.w * mSizeFraction, minSize, area.w);
auto diffSize = sliderSize - area.w * mSizeFraction;
canvas.rect({ area.x, area.y + (area.w - diffSize) * mPositionFraction, area.z, sliderSize }, col, 4.f);
}
public:
tp::halnf mScrollFactor = 0.f;
tp::halnf scrollInertia = 0.f;
bool mIsScrolling = false;
tp::halnf mSizeFraction = 1.f;
tp::halnf mPositionFraction = 0.f;
};
template <typename Events, typename Canvas>
requires(DrawableConcept<Canvas>)
class TrackWidget {
public:
TrackWidget(const Track* track = nullptr) :
mTrack(track) {
col.mColor.setAnimTime(0);
col.mColor.setNoTransition({ 0.15f, 0.15f, 0.15f, 0.f });
}; };
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) { template <typename Events, typename Canvas>
if (!mTrack) return; class TrackInfoWidget : public Widget<Events, Canvas> {
if (!areaParent.isOverlap(area)) return; struct SortType {
if (area.isInside(events.getPos())) { String text;
col.set({ 0.15f, 0.15f, 0.15f, 1.f }); bool dec = false;
insideDebug = true; bool inc = false;
} else {
col.set({ 0.15f, 0.15f, 0.15f, 0.f });
insideDebug = false;
}
}
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
if (!mTrack) return;
if (!areaParent.isOverlap(area)) return;
canvas.rect(area, col.get(), 4.f);
const tp::RectF imageArea = { area.x + marging, area.y + marging, area.w - marging * 2, area.w - marging * 2 };
canvas.rect(imageArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
const tp::RectF textArea = {
area.x + area.w + marging, area.y + marging, area.z - area.w - marging * 2, area.w - marging * 2
}; };
// canvas.rect(textArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
const tp::RectF textAreaName = { textArea.x, textArea.y, textArea.z, textArea.w * 0.5f }; public:
const tp::RectF textAreaAuthor = { textArea.x, textArea.y + textArea.w * 0.5f, textArea.z, textArea.w * 0.5f }; explicit TrackInfoWidget(const Track* track = nullptr) :
mTrack(track) {
canvas.text(mTrack->mName.read(), textAreaName, 15.f, Canvas::LC, 4.f, { 0.9f, 0.9f, 0.9f, 1.f }); items.append({ "Date Added" });
canvas.text(mTrack->mArtist.read(), textAreaAuthor, 12.f, Canvas::LC, 4.f, { 0.8f, 0.8f, 0.8f, 1.f }); items.append({ "Date Last Played" });
}
public:
tp::halnf marging = 5.f;
bool insideDebug = false;
tp::AnimColor col;
const Track* mTrack;
};
template <typename Events, typename Canvas>
requires(DrawableConcept<Canvas>)
class LibraryWidget {
public:
LibraryWidget(Library* lib, Player* player) :
mLibrary(lib),
mPlayer(player) {
for (auto track : mLibrary->mTraks) {
mTraks.append(TrackWidget<Events, Canvas>(&track.data()));
} }
mResizeWidget.value = 1000;
mCurrentTrackInfo.mPlayer = mPlayer;
}
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) { void proc(const Events&, const RectF& areaParent, const RectF& area) override {
mNeedRedraw = events.isEvent() || mCurrentTrackInfo.songFilter.IsActive(); this->mArea = area;
if (!mNeedRedraw) return; this->mVisible = area.isOverlap(areaParent);
if (!this->mVisible) return;
if (!mTrack) return;
// renderUI();
}
filter(); void draw(Canvas&) const override {
if (!this->mVisible) return;
if (!mTrack) return;
// canvas.rect(this->mArea, { 0.13f, 0.13f, 0.13f, 1.f }, 4.f);
}
mResizeWidget.max = aArea.z - trackInfoSize; void renderUI() {
mResizeWidget.min = 100; ImGui::SetNextWindowPos({ this->mArea.x, this->mArea.y });
ImGui::SetNextWindowSize({ this->mArea.z, this->mArea.w });
areaCP = { aArea.x, aArea.y + aArea.w - controllPanelSize, aArea.z, controllPanelSize }; ImGui::Begin(
area = { aArea.x, aArea.y, mResizeWidget.value, aArea.w - controllPanelSize }; "InfoWindow",
areaInfo = { nullptr,
mResizeWidget.value + resizeSize, aArea.y, aArea.z - mResizeWidget.value - resizeSize, aArea.w - controllPanelSize ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground |
}; ImGuiWindowFlags_NoResize
areaResize = { mResizeWidget.value, aArea.y, resizeSize, areaInfo.w }; );
mScroll.mSizeFraction = (area.w - 10) / (mTraksFiltered.size() * trackSize); if (mTrack) {
mScroll.mScrollFactor = 1.f / mTraksFiltered.size();
mScroll.proc(events, area, { area.x + area.z - scrollSize - 3, area.y + 10, scrollSize - 3.f, area.w - 20 }); ImGui::Text(" Load Status : %s", mLoadStatus ? "Loaded" : "Not Loaded");
mResizeWidget.proc(events, area, areaResize);
if (area.isInside(events.getPos())) { if (ImGui::Button("Play")) {
if (mPlayer->getPlayingId() != mTrack->mId) {
tp::halnf offset = mScroll.mPositionFraction * mTraksFiltered.size() * trackSize; mLoadStatus = mPlayer->startStreamTrack(mTrack->mId);
auto idx = 0; }
mPlayer->continuePlayback();
for (auto track : mTraksFiltered) {
auto trackArea =
tp::RectF{ area.x + 10, area.y + 10 + idx * trackSize - offset, area.z - 20 - scrollSize, trackSize - 5 };
track->proc(events, area, trackArea);
if (trackArea.isInside(events.getPos()) && events.isPressed()) {
mCurrentTrack.mTrack = track->mTrack;
mCurrentTrackInfo.mTrack = track->mTrack;
} }
idx++; if (mLoadStatus) {
ImGui::SameLine();
if (ImGui::Button("Stop")) {
mPlayer->freezePlayback();
}
auto songProgress = mPlayer->getPlaybackProgress();
auto vol = mPlayer->getVolume();
ImGui::Text("Load Progress : %f", mPlayer->getLoadProgress());
if (ImGui::SliderFloat("Progress", &songProgress, 0.f, 1.f)) {
mPlayer->setPlaybackProgress(songProgress);
}
if (ImGui::SliderFloat("Volume", &vol, 0.f, 1.f)) {
mPlayer->setVolume(vol);
}
}
} }
ImGui::Text("Song Info:");
if (mTrack) {
ImGui::Text("Name : %s", mTrack->mName.read());
ImGui::Text("Author : %s", mTrack->mArtist.read());
ImGui::Text("Love : %s", mTrack->mLoved ? "true" : "false");
ImGui::Text("Id : %lli", mTrack->mId);
ImGui::Text("Total Time : %lli", mTrack->mTotalTime);
ImGui::Text("Play Count : %lli", mTrack->mPlayCount);
ImGui::Text("Skip Count : %lli", mTrack->mSkipCount);
ImGui::Text("Date Added : %s", mTrack->mDateAdded.read());
ImGui::Text("Album : %s", mTrack->mAlbum.read());
} else {
ImGui::Text("Not Selected");
}
ImGui::Separator();
if (ImGui::Checkbox("Loved only", &filterLoved)) {
isSongFilterChanged |= true;
}
if (ImGui::SliderInt("Existing only", &filterExisting, 0, 3)) {
isSongFilterChanged |= true;
}
isSongFilterChanged |= songFilter.Draw("Song Filter");
sortFilter.Draw("Sorting Type");
for (int i = 0; i < items.size(); ++i) {
if (!sortFilter.PassFilter(items[i].text.read())) continue;
ImGui::PushID(i);
if (ImGui::Button("Inc")) {
items[i].inc = true;
}
ImGui::SameLine();
if (ImGui::Button("Dec")) {
items[i].dec = true;
}
ImGui::AlignTextToFramePadding();
ImGui::SameLine();
ImGui::Text("%s", items[i].text.read());
ImGui::PopID();
}
ImGui::End();
} }
debugPos = events.getPos(); public:
} ImGuiTextFilter songFilter{};
ImGuiTextFilter sortFilter{};
Buffer<SortType> items;
const Track* mTrack;
bool filterLoved = false;
Player* mPlayer = nullptr;
bool mLoadStatus = false;
bool isSongFilterChanged = true;
int filterExisting = 0; // all existing no-existing
};
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& aArea) { template <typename Events, typename Canvas>
class LibraryWidget : public Widget<Events, Canvas> {
public:
LibraryWidget(Library* lib, Player* player) {
mLibrary = (lib);
mPlayer = (player);
canvas.rect(aArea, { 0.1f, 0.1f, 0.1f, 1.f }); for (auto track : mLibrary->mTraks) {
mTracks.append(TrackWidget<Events, Canvas>(&track.data()));
}
tp::halnf offset = mScroll.mPositionFraction * mTraksFiltered.size() * trackSize; mCurrentTrackInfo.mPlayer = mPlayer;
auto idx = 0;
for (auto track : mTraksFiltered) {
auto const trackArea =
tp::RectF{ area.x + 10, area.y + 10 + idx * trackSize - offset, area.z - 20 - scrollSize, trackSize - 5 };
if (track->mTrack == mCurrentTrack.mTrack) canvas.rect(trackArea, { 0.2f, 0.2f, 0.2f, 0.5f }, 5);
track->draw(canvas, area, trackArea);
idx++;
} }
mScroll.draw(canvas, area, { area.x + area.z - scrollSize - 3, area.y + 10, scrollSize - 3.f, area.w - 20 }); void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
this->mArea = aArea;
this->mVisible = this->mArea.isOverlap(areaParent);
if (!this->mVisible) return;
// canvas.rect( areaInfo, { 0.04f, 0.04f, 0.04f, 1.f }); mCurrentTrackInfo.renderUI();
mCurrentTrackInfo.draw(canvas, aArea, areaInfo); mNeedRedraw = events.isEvent() || mCurrentTrackInfo.songFilter.IsActive();
mResizeWidget.draw(canvas, aArea, areaResize); if (!mNeedRedraw) return;
drawCP(canvas, aArea, areaCP); filter();
}
void drawCP(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) { mSplitView.proc(events, this->mArea, this->mArea);
canvas.rect(area, { 0.04f, 0.04f, 0.04f, 1.f }); mSongList.proc(events, this->mArea, mSplitView.getFirst());
mCurrentTrack.draw(canvas, area, area);
}
void filter() { for (auto track : mSongList.mContents) {
if (!mCurrentTrackInfo.isSongFilterChanged) return; auto trackWidget = (TrackWidget<Events, Canvas>*) track.data();
mTraksFiltered.clear(); if (trackWidget->mSelected) {
mCurrentTrackInfo.mTrack = trackWidget->mTrack;
for (auto track : mTraks) { }
if (!mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mName.read()) && !mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mArtist.read())) {
continue;
} }
if (mCurrentTrackInfo.filterLoved && !track->mTrack->mLoved) { mCurrentTrackInfo.proc(events, this->mArea, mSplitView.getSecond());
continue;
}
switch (mCurrentTrackInfo.filterExisting) { // mCurrentTrack.proc(events, this->mArea, mSplitView.getFirst());
case 1:
if (!track->mTrack->mExists) continue;
break;
case 2:
if (track->mTrack->mExists) continue;
break;
}
mTraksFiltered.append(&track.data());
} }
mCurrentTrackInfo.isSongFilterChanged = false; void draw(Canvas& canvas) const override {
} if (!this->mVisible) return;
private: canvas.rect(this->mArea, { 0.1f, 0.1f, 0.1f, 1.f });
tp::halnf scrollSize = 20;
tp::halnf trackInfoSize = 200;
tp::halnf controllPanelSize = 70;
tp::halnf trackSize = 60;
tp::Vec2F debugPos;
tp::halnf resizeSize = 10;
tp::RectF area; mSplitView.draw(canvas);
tp::RectF areaInfo; mSongList.draw(canvas);
tp::RectF areaCP; mCurrentTrackInfo.draw(canvas);
tp::RectF areaResize; // mCurrentTrack.proc(canvas);
}
tp::Buffer<TrackWidget<Events, Canvas>> mTraks; void filter() {
tp::Buffer<TrackWidget<Events, Canvas>*> mTraksFiltered; if (!mCurrentTrackInfo.isSongFilterChanged) return;
TrackWidget<Events, Canvas> mCurrentTrack; mSongList.mContents.clear();
TrackInfoWidget<Events, Canvas> mCurrentTrackInfo;
ScrollBarWidget<Events, Canvas> mScroll; for (auto track : mTracks) {
ResizerWidget<Events, Canvas> mResizeWidget; if (!mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mName.read()) && !mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mArtist.read())) {
continue;
}
Library* mLibrary; if (mCurrentTrackInfo.filterLoved && !track->mTrack->mLoved) {
Player* mPlayer; continue;
}
bool mNeedRedraw = false; switch (mCurrentTrackInfo.filterExisting) {
}; case 1:
if (!track->mTrack->mExists) continue;
break;
case 2:
if (track->mTrack->mExists) continue;
break;
}
mSongList.mContents.append(&track.data());
}
mCurrentTrackInfo.isSongFilterChanged = false;
}
private:
Library* mLibrary = nullptr;
Player* mPlayer = nullptr;
Buffer<TrackWidget<Events, Canvas>> mTracks;
SplitView<Events, Canvas> mSplitView;
ScrollableWindow<Events, Canvas> mSongList;
TrackInfoWidget<Events, Canvas> mCurrentTrackInfo;
TrackWidget<Events, Canvas> mCurrentTrack;
bool mNeedRedraw = false;
};
}

View file

@ -197,7 +197,7 @@ namespace tp {
const auto padding = this->getValue("Padding"); const auto padding = this->getValue("Padding");
mContentSize = mContents.last()->mArea.y - mContents.first()->mArea.y; mContentSize = mContents.last()->mArea.y - mContents.first()->mArea.y;
mContentSize += mContents.last()->mArea.w; mContentSize += mContents.last()->mArea.w;
mContentSize += (mContents.size()) * padding; mContentSize += 2 * padding;
} }
} }