#pragma once #include "Space.hpp" #include "ViewManager.hpp" #include "specter/PathButtons.hpp" #include "specter/Table.hpp" namespace metaforce { class ResourceBrowser : public Space, public specter::IPathButtonsBinding { struct State : Space::State { AT_DECL_DNA_YAMLV String<-1> path; Value columnSplits[3] = {0.0f, 0.7f, 0.9f}; enum class SortColumn { Name, Type, Size }; Value sortColumn = SortColumn::Name; Value sortDir = specter::SortDirection::Ascending; } m_state; const Space::State& spaceState() const override { return m_state; } hecl::ProjectPath m_path; std::vector m_comps; void pathButtonActivated(size_t idx) override; struct ResListingDataBind : specter::ITableDataBinding, specter::ITableStateBinding { ResourceBrowser& m_rb; struct Entry { hecl::SystemString m_path; std::string m_name; std::string m_type; std::string m_size; }; std::vector m_entries; std::string m_nameCol; std::string m_typeCol; std::string m_sizeCol; std::string m_dirStr; std::string m_projStr; std::string m_fileStr; size_t columnCount() const override { return 3; } size_t rowCount() const override { return m_entries.size(); } std::string_view header(size_t cIdx) const override { switch (cIdx) { case 0: return m_nameCol; case 1: return m_typeCol; case 2: return m_sizeCol; default: break; } return {}; } std::string_view cell(size_t cIdx, size_t rIdx) const override { switch (cIdx) { case 0: return m_entries.at(rIdx).m_name; case 1: return m_entries.at(rIdx).m_type; case 2: return m_entries.at(rIdx).m_size; default: break; } return {}; } bool columnSplitResizeAllowed() const override { return true; } float getColumnSplit(size_t cIdx) const override { return m_rb.m_state.columnSplits[cIdx]; } void setColumnSplit(size_t cIdx, float split) override { m_rb.m_state.columnSplits[cIdx] = split; } void updateListing(const hecl::DirectoryEnumerator& dEnum) { m_entries.clear(); m_entries.reserve(dEnum.size()); for (const hecl::DirectoryEnumerator::Entry& d : dEnum) { m_entries.emplace_back(); Entry& ent = m_entries.back(); ent.m_path = d.m_path; hecl::SystemUTF8Conv nameUtf8(d.m_name); ent.m_name = nameUtf8.str(); if (d.m_isDir) { if (hecl::SearchForProject(d.m_path)) ent.m_type = m_projStr; else ent.m_type = m_dirStr; } else { ent.m_type = m_fileStr; ent.m_size = hecl::HumanizeNumber(d.m_fileSz, 7, nullptr, int(hecl::HNScale::AutoScale), hecl::HNFlags::B | hecl::HNFlags::Decimal); } } m_needsUpdate = false; } bool m_needsUpdate = false; specter::SortDirection getSort(size_t& cIdx) const override { cIdx = size_t(m_rb.m_state.sortColumn); if (cIdx > 2) cIdx = 0; return m_rb.m_state.sortDir; } void setSort(size_t cIdx, specter::SortDirection dir) override { m_rb.m_state.sortDir = dir; m_rb.m_state.sortColumn = State::SortColumn(cIdx); m_needsUpdate = true; } void setSelectedRow(size_t rIdx) override {} void rowActivated(size_t rIdx) override {} ResListingDataBind(ResourceBrowser& rb, const specter::IViewManager& vm) : m_rb(rb) { m_nameCol = vm.translate(); m_typeCol = vm.translate(); m_sizeCol = vm.translate(); m_dirStr = vm.translate(); m_projStr = vm.translate(); m_fileStr = vm.translate(); } } m_resListingBind; struct View : specter::View { ResourceBrowser& m_ro; specter::ViewChild> m_resListing; View(ResourceBrowser& ro, specter::ViewResources& res) : specter::View(res, ro.m_vm.rootView()), m_ro(ro) { m_resListing.m_view.reset(new specter::Table(res, *this, &ro.m_resListingBind, &ro.m_resListingBind, 3)); } void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey) override; void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey) override; void mouseMove(const boo::SWindowCoord&) override; void mouseLeave(const boo::SWindowCoord&) override; void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) override; void draw(boo::IGraphicsCommandQueue* gfxQ) override; }; std::unique_ptr m_view; std::unique_ptr m_pathButtons; public: ResourceBrowser(ViewManager& vm, Space* parent) : Space(vm, Class::ResourceBrowser, parent), m_resListingBind(*this, vm) { reloadState(); } ResourceBrowser(ViewManager& vm, Space* parent, const ResourceBrowser& other) : ResourceBrowser(vm, parent) { m_state = other.m_state; reloadState(); } ResourceBrowser(ViewManager& vm, Space* parent, ConfigReader& r) : ResourceBrowser(vm, parent) { m_state.read(r); reloadState(); } void reloadState() override { hecl::ProjectPath pp(*m_vm.project(), m_state.path); if (m_state.path.empty() || pp.isNone()) { m_state.path = m_vm.project()->getProjectWorkingPath().getRelativePathUTF8(); navigateToPath(hecl::ProjectPath(*m_vm.project(), m_state.path)); } else navigateToPath(pp); } void think() override { if (m_resListingBind.m_needsUpdate) reloadState(); } bool navigateToPath(const hecl::ProjectPath& path); Space* copy(Space* parent) const override { return new ResourceBrowser(m_vm, parent, *this); } void buildToolbarView(specter::ViewResources& res, specter::Toolbar& tb) override { m_pathButtons.reset(new specter::PathButtons(res, tb, *this, true)); tb.push_back(m_pathButtons.get(), 1); reloadState(); } specter::View* buildContentView(specter::ViewResources& res) override { m_view.reset(new View(*this, res)); return m_view.get(); } bool usesToolbar() const override { return true; } unsigned toolbarUnits() const override { return 2; } }; } // namespace metaforce