diff --git a/LibraryViewer/CMakeLists.txt b/LibraryViewer/CMakeLists.txt index 1bb13fe..becb208 100644 --- a/LibraryViewer/CMakeLists.txt +++ b/LibraryViewer/CMakeLists.txt @@ -2,7 +2,7 @@ project(LibraryViewer) ### ---------------------- Externals --------------------- ### 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 --------------------- ### 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 ${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}/") ### -------------------------- Applications -------------------------- ### diff --git a/LibraryViewer/applications/Entry.cpp b/LibraryViewer/applications/Entry.cpp index 029d55e..b394300 100644 --- a/LibraryViewer/applications/Entry.cpp +++ b/LibraryViewer/applications/Entry.cpp @@ -3,22 +3,27 @@ #include "Player.hpp" // 1) artworks -// 2) how to easy add more songs? +// 2) how to easily add more songs? // 3) GUi : // - seeker // - song idx -// - non existing highlight +// - non-existing highlight // - prev next // - remove debug gui // 4) queue & repeat & shuffle... // 5) new database with history void runApp() { + Player player; Library library; library.loadJson(getHome() + "Library.json"); library.checkExisting(); - LibraryWidget gui(&library, &player); + + tp::GlobalGUIConfig config; + tp::gGlobalGUIConfig = &config; + + tp::LibraryWidget gui(&library, &player); auto window = tp::Window::createWindow(800, 600, "Window 1"); @@ -29,7 +34,7 @@ void runApp() { auto area = window->getCanvas().getAvaliableArea(); 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); diff --git a/LibraryViewer/library/tracks/2437.flac b/LibraryViewer/library/tracks/2437.flac index 560ceba..1f66c7c 100644 Binary files a/LibraryViewer/library/tracks/2437.flac and b/LibraryViewer/library/tracks/2437.flac differ diff --git a/LibraryViewer/library/tracks/2439.flac b/LibraryViewer/library/tracks/2439.flac new file mode 100644 index 0000000..1f66c7c Binary files /dev/null and b/LibraryViewer/library/tracks/2439.flac differ diff --git a/LibraryViewer/library/tracks/6477.flac b/LibraryViewer/library/tracks/6477.flac new file mode 100644 index 0000000..1f66c7c Binary files /dev/null and b/LibraryViewer/library/tracks/6477.flac differ diff --git a/LibraryViewer/library/tracks/6479.flac b/LibraryViewer/library/tracks/6479.flac new file mode 100644 index 0000000..1f66c7c Binary files /dev/null and b/LibraryViewer/library/tracks/6479.flac differ diff --git a/LibraryViewer/private/Library.cpp b/LibraryViewer/private/Library.cpp index 3fd1d21..70fb249 100644 --- a/LibraryViewer/private/Library.cpp +++ b/LibraryViewer/private/Library.cpp @@ -1,12 +1,13 @@ #include "Library.hpp" #include "LocalConnection.hpp" +#include "WidgetBase.hpp" #include "picojson.h" #include namespace tp { - static ModuleManifest* deps[] = { &gModuleGraphics, &gModuleConnection, nullptr }; + static ModuleManifest* deps[] = { &gModuleGraphics, &gModuleConnection, &gModuleWidgets, nullptr }; ModuleManifest gModuleLibraryViewer = ModuleManifest("LibraryViewer", nullptr, nullptr, deps); } @@ -23,15 +24,13 @@ tp::String getHome() { if (envVarValue != nullptr) { tp::String out; out = (int1*) (std::string(envVarValue) + "/").c_str(); - return out; + return out; } else { - return ""; + return "library/"; } } -tp::String getSongLocalPath(SongId id) { - return getHome() + "tracks/" + tp::String((tp::alni)id); -} +tp::String getSongLocalPath(SongId id) { return getHome() + "tracks/" + tp::String((tp::alni) id); } SONG_FORMAT getSongFormat(const String& localPath) { std::filesystem::path wavFormat((localPath + ".wav").read()); @@ -56,21 +55,24 @@ bool Library::loadJson(const String& path) { libraryFileMem.reserve(libraryFile.size()); libraryFile.readBytes(libraryFileMem.getBuff(), libraryFile.size()); - + std::string json = libraryFileMem.getBuff(); picojson::value jsonNode; 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; } - auto & traks = jsonNode.get(); - for (auto & trackNode : traks) { + auto& traks = jsonNode.get(); + for (auto& trackNode : traks) { Track newTrack; - auto & track = trackNode.get(); - for (auto & trackProperty : track) { + auto& track = trackNode.get(); + for (auto& trackProperty : track) { if (PROP(Name)) newTrack.mName = AS_STR; else if (PROP(Artist)) newTrack.mArtist = AS_STR; else if (PROP(Track ID)) newTrack.mId = AS_INT; diff --git a/LibraryViewer/public/GUI.hpp b/LibraryViewer/public/GUI.hpp index ee825dc..0233fef 100644 --- a/LibraryViewer/public/GUI.hpp +++ b/LibraryViewer/public/GUI.hpp @@ -4,459 +4,295 @@ #include "Player.hpp" #include "Widgets.hpp" -template -class ResizerWidget { -public: - ResizerWidget() = default; +#include "imgui.h" - void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) { - if (!areaParent.isOverlap(area)) { - resizing = false; - return; - } +namespace tp { - hover = area.isInside(events.getPos()); + template + class TrackWidget : public Widget { + 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) { - resizing = true; - } else if (!events.isDown()) { - resizing = false; - } + void proc(const Events& events, const RectF& areaParent, const RectF& area) override { + mSelected = false; + this->mArea = area; + this->mVisible = area.isOverlap(areaParent); + if (!this->mVisible) return; - value = area.x; - - if (resizing) { - tp::halnf pos = events.getPos().x; - auto diff = (pos - value - area.z / 2.f); - 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 - requires(DrawableConcept) -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 items; - const Track* mTrack; - bool filterLoved = false; - Player* mPlayer = nullptr; - bool mLoadStatus = false; - bool isSongFilterChanged = true; - int filterExisting = 0; // all existing no-existing -}; - -template - requires(DrawableConcept) -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; + if (!mTrack) return; + if (!areaParent.isOverlap(area)) return; + if (area.isInside(events.getPos())) { + col.set({ 0.15f, 0.15f, 0.15f, 1.f }); + mSelected = events.isReleased(); } else { - scrollInertia = -scrollInertia + offset; + col.set({ 0.15f, 0.15f, 0.15f, 0.f }); } } - if (tp::abs(scrollInertia) > 0.1f) { - auto offset = scrollInertia * mScrollFactor; - mPositionFraction += offset; - mPositionFraction = tp::clamp(mPositionFraction, 0.f, 1.f - mSizeFraction); - scrollInertia *= 0.f; - return; + void draw(Canvas& canvas) const override { + if (!this->mVisible) return; + if (!mTrack) return; + + canvas.rect(this->mArea, col.get(), 4.f); + + 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())) { - mIsScrolling = true; - } else if (!events.isDown()) { - mIsScrolling = 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 - requires(DrawableConcept) -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 }); + public: + halnf margin = 5.f; + AnimColor col; + const Track* mTrack; + bool mSelected = false; }; - void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) { - if (!mTrack) return; - if (!areaParent.isOverlap(area)) return; - if (area.isInside(events.getPos())) { - col.set({ 0.15f, 0.15f, 0.15f, 1.f }); - insideDebug = true; - } 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 + template + class TrackInfoWidget : public Widget { + struct SortType { + String text; + bool dec = false; + bool inc = false; }; - // 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 }; - const tp::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 }); - } - -public: - tp::halnf marging = 5.f; - bool insideDebug = false; - tp::AnimColor col; - const Track* mTrack; -}; - -template - requires(DrawableConcept) -class LibraryWidget { -public: - LibraryWidget(Library* lib, Player* player) : - mLibrary(lib), - mPlayer(player) { - for (auto track : mLibrary->mTraks) { - mTraks.append(TrackWidget(&track.data())); + public: + explicit TrackInfoWidget(const Track* track = nullptr) : + mTrack(track) { + items.append({ "Date Added" }); + items.append({ "Date Last Played" }); } - mResizeWidget.value = 1000; - mCurrentTrackInfo.mPlayer = mPlayer; - } - void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) { - mNeedRedraw = events.isEvent() || mCurrentTrackInfo.songFilter.IsActive(); - if (!mNeedRedraw) return; + void proc(const Events&, const RectF& areaParent, const RectF& area) override { + this->mArea = area; + 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; - mResizeWidget.min = 100; + void renderUI() { + 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 }; - area = { aArea.x, aArea.y, mResizeWidget.value, aArea.w - controllPanelSize }; - areaInfo = { - mResizeWidget.value + resizeSize, aArea.y, aArea.z - mResizeWidget.value - resizeSize, aArea.w - controllPanelSize - }; - areaResize = { mResizeWidget.value, aArea.y, resizeSize, areaInfo.w }; + ImGui::Begin( + "InfoWindow", + nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground | + ImGuiWindowFlags_NoResize + ); - mScroll.mSizeFraction = (area.w - 10) / (mTraksFiltered.size() * trackSize); - mScroll.mScrollFactor = 1.f / mTraksFiltered.size(); + if (mTrack) { - mScroll.proc(events, area, { area.x + area.z - scrollSize - 3, area.y + 10, scrollSize - 3.f, area.w - 20 }); - mResizeWidget.proc(events, area, areaResize); + ImGui::Text(" Load Status : %s", mLoadStatus ? "Loaded" : "Not Loaded"); - if (area.isInside(events.getPos())) { - - tp::halnf offset = mScroll.mPositionFraction * mTraksFiltered.size() * trackSize; - auto idx = 0; - - 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; + if (ImGui::Button("Play")) { + if (mPlayer->getPlayingId() != mTrack->mId) { + mLoadStatus = mPlayer->startStreamTrack(mTrack->mId); + } + mPlayer->continuePlayback(); } - 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 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 + class LibraryWidget : public Widget { + 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(&track.data())); + } - tp::halnf offset = mScroll.mPositionFraction * mTraksFiltered.size() * trackSize; - 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++; + mCurrentTrackInfo.mPlayer = mPlayer; } - 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.draw(canvas, aArea, areaInfo); + mCurrentTrackInfo.renderUI(); + 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) { - canvas.rect(area, { 0.04f, 0.04f, 0.04f, 1.f }); - mCurrentTrack.draw(canvas, area, area); - } + mSplitView.proc(events, this->mArea, this->mArea); + mSongList.proc(events, this->mArea, mSplitView.getFirst()); - void filter() { - if (!mCurrentTrackInfo.isSongFilterChanged) return; - mTraksFiltered.clear(); - - for (auto track : mTraks) { - if (!mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mName.read()) && !mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mArtist.read())) { - continue; + for (auto track : mSongList.mContents) { + auto trackWidget = (TrackWidget*) track.data(); + if (trackWidget->mSelected) { + mCurrentTrackInfo.mTrack = trackWidget->mTrack; + } } - if (mCurrentTrackInfo.filterLoved && !track->mTrack->mLoved) { - continue; - } + mCurrentTrackInfo.proc(events, this->mArea, mSplitView.getSecond()); - switch (mCurrentTrackInfo.filterExisting) { - case 1: - if (!track->mTrack->mExists) continue; - break; - - case 2: - if (track->mTrack->mExists) continue; - break; - } - - mTraksFiltered.append(&track.data()); + // mCurrentTrack.proc(events, this->mArea, mSplitView.getFirst()); } - mCurrentTrackInfo.isSongFilterChanged = false; - } + void draw(Canvas& canvas) const override { + if (!this->mVisible) return; -private: - tp::halnf scrollSize = 20; - tp::halnf trackInfoSize = 200; - tp::halnf controllPanelSize = 70; - tp::halnf trackSize = 60; - tp::Vec2F debugPos; - tp::halnf resizeSize = 10; + canvas.rect(this->mArea, { 0.1f, 0.1f, 0.1f, 1.f }); - tp::RectF area; - tp::RectF areaInfo; - tp::RectF areaCP; - tp::RectF areaResize; + mSplitView.draw(canvas); + mSongList.draw(canvas); + mCurrentTrackInfo.draw(canvas); + // mCurrentTrack.proc(canvas); + } - tp::Buffer> mTraks; - tp::Buffer*> mTraksFiltered; + void filter() { + if (!mCurrentTrackInfo.isSongFilterChanged) return; - TrackWidget mCurrentTrack; - TrackInfoWidget mCurrentTrackInfo; + mSongList.mContents.clear(); - ScrollBarWidget mScroll; - ResizerWidget mResizeWidget; + for (auto track : mTracks) { + if (!mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mName.read()) && !mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mArtist.read())) { + continue; + } - Library* mLibrary; - Player* mPlayer; + if (mCurrentTrackInfo.filterLoved && !track->mTrack->mLoved) { + continue; + } - bool mNeedRedraw = false; -}; \ No newline at end of file + 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> mTracks; + + SplitView mSplitView; + ScrollableWindow mSongList; + TrackInfoWidget mCurrentTrackInfo; + TrackWidget mCurrentTrack; + + bool mNeedRedraw = false; + }; +} \ No newline at end of file diff --git a/Widgets/public/ScrollableWidget.hpp b/Widgets/public/ScrollableWidget.hpp index 8fcd89b..00d6916 100644 --- a/Widgets/public/ScrollableWidget.hpp +++ b/Widgets/public/ScrollableWidget.hpp @@ -197,7 +197,7 @@ namespace tp { const auto padding = this->getValue("Padding"); mContentSize = mContents.last()->mArea.y - mContents.first()->mArea.y; mContentSize += mContents.last()->mArea.w; - mContentSize += (mContents.size()) * padding; + mContentSize += 2 * padding; } }