diff --git a/specter/include/Specter/FileBrowser.hpp b/specter/include/Specter/FileBrowser.hpp index c20b56a99..9ccadab23 100644 --- a/specter/include/Specter/FileBrowser.hpp +++ b/specter/include/Specter/FileBrowser.hpp @@ -15,6 +15,16 @@ namespace Specter class FileBrowser : public ModalWindow { +public: + enum class Type + { + SaveFile, + OpenFile, + OpenDirectory, + OpenHECLProject + }; +private: + Type m_type; std::vector m_comps; class LeftSide : public View @@ -68,6 +78,7 @@ class FileBrowser : public ModalWindow void activated(const boo::SWindowCoord&) {m_fb.cancelActivated();} } m_cancel; + int m_pathButtonPending = -1; void pathButtonActivated(size_t idx); struct PathButton : IButtonBinding { @@ -81,7 +92,7 @@ class FileBrowser : public ModalWindow m_button.m_view.reset(new Button(res, fb, this, utf8View)); } const char* name() const {return m_button.m_view->getText().c_str();} - void activated(const boo::SWindowCoord&) {m_fb.pathButtonActivated(m_idx);} + void activated(const boo::SWindowCoord&) {m_fb.m_pathButtonPending = m_idx;} }; friend struct PathButton; std::vector m_pathButtons; @@ -97,29 +108,23 @@ class FileBrowser : public ModalWindow } } m_fileFieldBind; - struct TableDataBind : ITableDataBinding + struct FileListingDataBind : ITableDataBinding { - std::string m_nameCol = "Name"; - std::vector m_names = {"One", "Two", "Three"}; - - std::string m_typeCol = "Type"; - std::vector m_types = {"t1", "t2", "t3"}; - - std::string m_sizeCol = "Size"; - std::vector m_sizes = {"s1", "s2", "s3"}; - - TableDataBind() + struct Entry { - for (int i=0 ; i<100 ; ++i) - { - m_names.push_back(HECL::Format("%d", i)); - m_types.push_back(HECL::Format("%d", i)); - m_sizes.push_back(HECL::Format("%d", i)); - } - } + HECL::SystemString m_path; + std::string m_name; + std::string m_type; + std::string m_size; + }; + std::vector m_entries; + + std::string m_nameCol = "Name"; + std::string m_typeCol = "Type"; + std::string m_sizeCol = "Size"; size_t columnCount() const {return 3;} - size_t rowCount() const {return 103;} + size_t rowCount() const {return m_entries.size();} const std::string* header(size_t cIdx) const { @@ -141,23 +146,78 @@ class FileBrowser : public ModalWindow switch (cIdx) { case 0: - return &m_names.at(rIdx); + return &m_entries.at(rIdx).m_name; case 1: - return &m_types.at(rIdx); + return &m_entries.at(rIdx).m_type; case 2: - return &m_sizes.at(rIdx); + return &m_entries.at(rIdx).m_size; default: break; } return nullptr; } + + 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::SystemUTF8View nameUtf8(d.m_name); + ent.m_name = nameUtf8.str(); + if (d.m_isDir) + ent.m_type = "Directory"; + else + { + ent.m_type = "File"; + ent.m_size = HECL::HumanizeNumber(d.m_fileSz, 7, nullptr, int(HECL::HNScale::AutoScale), + HECL::HNFlags::B | HECL::HNFlags::Decimal); + } + } + } + } m_fileListingBind; ViewChild> m_fileListing; -public: - FileBrowser(ViewResources& res, View& parentView, const std::string& title) - : FileBrowser(res, parentView, title, HECL::GetcwdStr()) {} - FileBrowser(ViewResources& res, View& parentView, const std::string& title, const HECL::SystemString& initialPath); + struct BookmarkDataBind : ITableDataBinding + { + struct Entry + { + HECL::SystemString m_path; + std::string m_name; + }; + std::vector m_entries; + size_t columnCount() const {return 1;} + size_t rowCount() const {return m_entries.size();} + + const std::string* cell(size_t, size_t rIdx) const + { + return &m_entries.at(rIdx).m_name; + } + }; + + BookmarkDataBind m_systemBookmarkBind; + std::unique_ptr m_systemBookmarksLabel; + ViewChild> m_systemBookmarks; + + BookmarkDataBind m_projectBookmarkBind; + std::unique_ptr m_projectBookmarksLabel; + ViewChild> m_projectBookmarks; + + BookmarkDataBind m_recentBookmarkBind; + std::unique_ptr m_recentBookmarksLabel; + ViewChild> m_recentBookmarks; + +public: + FileBrowser(ViewResources& res, View& parentView, const std::string& title, Type type) + : FileBrowser(res, parentView, title, type, HECL::GetcwdStr()) {} + FileBrowser(ViewResources& res, View& parentView, const std::string& title, Type type, const HECL::SystemString& initialPath); + + void navigateToPath(const HECL::SystemString& path); void updateContentOpacity(float opacity); void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey); diff --git a/specter/include/Specter/FontCache.hpp b/specter/include/Specter/FontCache.hpp index 8e643e54f..1b60bac76 100644 --- a/specter/include/Specter/FontCache.hpp +++ b/specter/include/Specter/FontCache.hpp @@ -76,8 +76,8 @@ public: float m_uv[4]; atInt32 m_leftPadding; atInt32 m_advance; - atUint32 m_width; - atUint32 m_height; + atInt32 m_width; + atInt32 m_height; atInt32 m_verticalOffset; }; diff --git a/specter/include/Specter/ScrollView.hpp b/specter/include/Specter/ScrollView.hpp index 606f51bfb..1338ad17b 100644 --- a/specter/include/Specter/ScrollView.hpp +++ b/specter/include/Specter/ScrollView.hpp @@ -29,6 +29,7 @@ private: boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */ boo::IShaderDataBinding* m_shaderBinding = nullptr; + bool _scroll(const boo::SScrollDelta& scroll); public: ScrollView(ViewResources& res, View& parentView, Style style); void setContentView(View* v) diff --git a/specter/lib/FileBrowser.cpp b/specter/lib/FileBrowser.cpp index 5b2b752bf..6f3671a42 100644 --- a/specter/lib/FileBrowser.cpp +++ b/specter/lib/FileBrowser.cpp @@ -40,12 +40,12 @@ static std::vector PathComponents(const HECL::SystemString& } FileBrowser::FileBrowser(ViewResources& res, View& parentView, const std::string& title, - const HECL::SystemString& initialPath) + Type type, const HECL::SystemString& initialPath) : ModalWindow(res, parentView, RectangleConstraint(BROWSER_MIN_WIDTH * res.pixelFactor(), BROWSER_MIN_HEIGHT * res.pixelFactor(), RectangleConstraint::Test::Minimum, RectangleConstraint::Test::Minimum)), - m_comps(PathComponents(initialPath)), + m_type(type), m_left(*this, res), m_right(*this, res), m_ok(*this, res, title), @@ -55,14 +55,19 @@ FileBrowser::FileBrowser(ViewResources& res, View& parentView, const std::string commitResources(res); setBackground({0,0,0,0.5}); - m_pathButtons.reserve(m_comps.size()); - size_t idx = 0; - for (const HECL::SystemString& c : m_comps) - m_pathButtons.emplace_back(*this, res, idx++, c); - m_fileField.m_view.reset(new TextField(res, *this, &m_fileFieldBind)); m_fileListing.m_view.reset(new Table(res, *this, &m_fileListingBind)); - m_fileListing.m_view->setBackground(Zeus::CColor::skBlue); + m_systemBookmarks.m_view.reset(new Table(res, *this, &m_systemBookmarkBind)); + m_systemBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont)); + m_systemBookmarksLabel->typesetGlyphs("System Locations:", res.themeData().uiText()); + m_projectBookmarks.m_view.reset(new Table(res, *this, &m_projectBookmarkBind)); + m_projectBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont)); + m_projectBookmarksLabel->typesetGlyphs("Recent Projects:", res.themeData().uiText()); + m_recentBookmarks.m_view.reset(new Table(res, *this, &m_recentBookmarkBind)); + m_recentBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont)); + m_recentBookmarksLabel->typesetGlyphs("Recent Files:", res.themeData().uiText()); + + navigateToPath(initialPath); m_split.m_view.reset(new SplitView(res, *this, SplitView::Axis::Vertical, 200 * res.pixelFactor(), 400 * res.pixelFactor())); @@ -73,6 +78,43 @@ FileBrowser::FileBrowser(ViewResources& res, View& parentView, const std::string updateContentOpacity(0.0); } +void FileBrowser::navigateToPath(const HECL::SystemString& path) +{ + HECL::Sstat theStat; + if (HECL::Stat(path.c_str(), &theStat)) + return; + + m_comps = PathComponents(path); + if (S_ISREG(theStat.st_mode)) + { + HECL::SystemUTF8View utf8(m_comps.back()); + m_fileField.m_view->setText(utf8); + m_comps.pop_back(); + } + + HECL::SystemString dir; + bool needSlash = false; + for (const HECL::SystemString& d : m_comps) + { + if (needSlash) + dir += '/'; + needSlash = true; + dir += d; + } + HECL::DirectoryEnumerator dEnum(dir); + m_fileListingBind.updateListing(dEnum); + m_fileListing.m_view->updateData(); + + m_pathButtons.clear(); + m_pathButtons.reserve(m_comps.size()); + size_t idx = 0; + ViewResources& res = rootView().viewRes(); + for (const HECL::SystemString& c : m_comps) + m_pathButtons.emplace_back(*this, res, idx++, c); + + updateSize(); +} + void FileBrowser::updateContentOpacity(float opacity) { Zeus::CColor color = Zeus::CColor::lerp({1,1,1,0}, {1,1,1,1}, opacity); @@ -83,6 +125,12 @@ void FileBrowser::updateContentOpacity(float opacity) m_fileListing.m_view->setMultiplyColor(color); m_ok.m_button.m_view->setMultiplyColor(color); m_cancel.m_button.m_view->setMultiplyColor(color); + m_systemBookmarks.m_view->setMultiplyColor(color); + m_systemBookmarksLabel->setMultiplyColor(color); + m_projectBookmarks.m_view->setMultiplyColor(color); + m_projectBookmarksLabel->setMultiplyColor(color); + m_recentBookmarks.m_view->setMultiplyColor(color); + m_recentBookmarksLabel->setMultiplyColor(color); } void FileBrowser::okActivated() @@ -96,6 +144,22 @@ void FileBrowser::cancelActivated() void FileBrowser::pathButtonActivated(size_t idx) { + if (idx >= m_comps.size()) + return; + + HECL::SystemString dir; + bool needSlash = false; + size_t i = 0; + for (const HECL::SystemString& d : m_comps) + { + if (needSlash) + dir += '/'; + needSlash = true; + dir += d; + if (++i > idx) + break; + } + navigateToPath(dir); } void FileBrowser::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod) @@ -115,9 +179,17 @@ void FileBrowser::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton butt { if (closed()) return; + m_split.mouseUp(coord, button, mod); + for (PathButton& b : m_pathButtons) b.m_button.mouseUp(coord, button, mod); + if (m_pathButtonPending >= 0) + { + pathButtonActivated(m_pathButtonPending); + m_pathButtonPending = -1; + } + m_fileField.mouseUp(coord, button, mod); m_fileListing.mouseUp(coord, button, mod); m_ok.m_button.mouseUp(coord, button, mod); @@ -182,11 +254,39 @@ void FileBrowser::resized(const boo::SWindowRect& root, const boo::SWindowRect& centerRect.size[0] -= 4 * pf; centerRect.size[1] -= 4 * pf; - m_split.m_view->resized(root, centerRect); + if (m_split.m_view) + m_split.m_view->resized(root, centerRect); } void FileBrowser::LeftSide::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) { + float pf = rootView().viewRes().pixelFactor(); + + int div = (sub.size[1] - BROWSER_MARGIN * pf) / 3; + + boo::SWindowRect bookmarkRect = sub; + bookmarkRect.size[0] -= BROWSER_MARGIN * 2 * pf; + bookmarkRect.size[1] = div; + bookmarkRect.location[0] += BROWSER_MARGIN * pf; + bookmarkRect.location[1] = sub.location[1] + BROWSER_MARGIN * pf + div * 2; + + boo::SWindowRect labelRect = bookmarkRect; + labelRect.size[1] = 20; + labelRect.location[1] += div - 16 * pf; + + m_fb.m_systemBookmarks.m_view->resized(root, bookmarkRect); + m_fb.m_systemBookmarksLabel->resized(root, labelRect); + bookmarkRect.location[1] -= div; + labelRect.location[1] -= div; + + m_fb.m_projectBookmarks.m_view->resized(root, bookmarkRect); + m_fb.m_projectBookmarksLabel->resized(root, labelRect); + bookmarkRect.location[1] -= div; + labelRect.location[1] -= div; + + m_fb.m_recentBookmarks.m_view->resized(root, bookmarkRect); + m_fb.m_recentBookmarksLabel->resized(root, labelRect); + } void FileBrowser::RightSide::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) @@ -230,6 +330,9 @@ void FileBrowser::think() ModalWindow::think(); m_fileField.m_view->think(); m_fileListing.m_view->think(); + m_systemBookmarks.m_view->think(); + m_projectBookmarks.m_view->think(); + m_recentBookmarks.m_view->think(); } void FileBrowser::draw(boo::IGraphicsCommandQueue* gfxQ) @@ -240,6 +343,12 @@ void FileBrowser::draw(boo::IGraphicsCommandQueue* gfxQ) void FileBrowser::LeftSide::draw(boo::IGraphicsCommandQueue* gfxQ) { + m_fb.m_systemBookmarks.m_view->draw(gfxQ); + m_fb.m_systemBookmarksLabel->draw(gfxQ); + m_fb.m_projectBookmarks.m_view->draw(gfxQ); + m_fb.m_projectBookmarksLabel->draw(gfxQ); + m_fb.m_recentBookmarks.m_view->draw(gfxQ); + m_fb.m_recentBookmarksLabel->draw(gfxQ); } void FileBrowser::RightSide::draw(boo::IGraphicsCommandQueue* gfxQ) diff --git a/specter/lib/FontCache.cpp b/specter/lib/FontCache.cpp index 3b5e86201..18fd151f7 100644 --- a/specter/lib/FontCache.cpp +++ b/specter/lib/FontCache.cpp @@ -141,7 +141,7 @@ void FontAtlas::buildKernTable(FT_Face face) if (!ttface->kern_table) return; Athena::io::MemoryReader r(ttface->kern_table, ttface->kern_table_size); - std::unordered_map>>::iterator it = m_kernAdjs.end(); + auto it = m_kernAdjs.end(); atUint32 nSubs = r.readUint32Big(); for (atUint32 i=0 ; isetDrawPrimitive(boo::Primitive::TriStrips); gfxQ->setScissor(m_scissorRect); - size_t rows = std::min(m_visibleRows, m_t.m_rows); - gfxQ->draw(1, rows * m_t.m_columns * 6 - 2); + gfxQ->draw(1, m_visibleRows * m_t.m_columns * 6 - 2); for (auto& col : m_t.m_cellViews) { size_t idx = 0; @@ -296,9 +295,12 @@ void Table::RowsView::draw(boo::IGraphicsCommandQueue* gfxQ) } gfxQ->setScissor(rootView().subRect()); - for (std::unique_ptr& hv : m_t.m_headerViews) - if (hv) - hv->draw(gfxQ); + if (m_t.m_header) + { + for (std::unique_ptr& hv : m_t.m_headerViews) + if (hv) + hv->draw(gfxQ); + } } void Table::CellView::draw(boo::IGraphicsCommandQueue* gfxQ) diff --git a/specter/lib/TextField.cpp b/specter/lib/TextField.cpp index d2ee94f52..d79211173 100644 --- a/specter/lib/TextField.cpp +++ b/specter/lib/TextField.cpp @@ -45,7 +45,6 @@ TextField::TextField(ViewResources& res, View& parentView, IStringBinding* strBi m_bVertsBuf->load(m_verts, sizeof(m_verts)); m_text.reset(new TextView(res, *this, res.m_mainFont, TextView::Alignment::Left, 1024)); - setText("ใƒ†ใ‚นใƒˆ"); } void TextField::_setText()