mirror of https://github.com/AxioDL/metaforce.git
FileBrowser column resizing and navigation
This commit is contained in:
parent
35ad8827a7
commit
8959431a2b
|
@ -28,6 +28,7 @@ private:
|
|||
Type m_type;
|
||||
HECL::SystemString m_path;
|
||||
std::vector<HECL::SystemString> m_comps;
|
||||
bool m_showingHidden = false;
|
||||
|
||||
class LeftSide : public View
|
||||
{
|
||||
|
@ -49,7 +50,7 @@ private:
|
|||
|
||||
ViewChild<std::unique_ptr<SplitView>> m_split;
|
||||
|
||||
void okActivated();
|
||||
void okActivated(bool viaButton);
|
||||
struct OKButton : IButtonBinding
|
||||
{
|
||||
FileBrowser& m_fb;
|
||||
|
@ -62,7 +63,7 @@ private:
|
|||
RectangleConstraint(100 * res.pixelFactor(), -1, RectangleConstraint::Test::Minimum)));
|
||||
}
|
||||
const char* name() const {return m_text.c_str();}
|
||||
void activated(const boo::SWindowCoord&) {m_fb.okActivated();}
|
||||
void activated(const boo::SWindowCoord&) {m_fb.okActivated(true);}
|
||||
} m_ok;
|
||||
|
||||
void cancelActivated();
|
||||
|
@ -115,6 +116,8 @@ private:
|
|||
|
||||
struct FileListingDataBind : ITableDataBinding, ITableStateBinding
|
||||
{
|
||||
FileBrowser& m_fb;
|
||||
|
||||
struct Entry
|
||||
{
|
||||
HECL::SystemString m_path;
|
||||
|
@ -164,6 +167,20 @@ private:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
float m_columnSplits[3] = {0.0, 0.7, 0.9};
|
||||
|
||||
bool columnSplitResizeAllowed() const {return true;}
|
||||
|
||||
float getColumnSplit(size_t cIdx) const
|
||||
{
|
||||
return m_columnSplits[cIdx];
|
||||
}
|
||||
|
||||
void setColumnSplit(size_t cIdx, float split)
|
||||
{
|
||||
m_columnSplits[cIdx] = split;
|
||||
}
|
||||
|
||||
void updateListing(const HECL::DirectoryEnumerator& dEnum)
|
||||
{
|
||||
m_entries.clear();
|
||||
|
@ -210,10 +227,20 @@ private:
|
|||
|
||||
void setSelectedRow(size_t rIdx)
|
||||
{
|
||||
|
||||
if (rIdx != -1)
|
||||
m_fb.m_fileField.m_view->setText(m_entries.at(rIdx).m_name);
|
||||
else
|
||||
m_fb.m_fileField.m_view->setText("");
|
||||
m_fb.m_fileField.m_view->clearErrorState();
|
||||
}
|
||||
|
||||
FileListingDataBind(const IViewManager& vm)
|
||||
void rowActivated(size_t rIdx)
|
||||
{
|
||||
m_fb.okActivated(false);
|
||||
}
|
||||
|
||||
FileListingDataBind(FileBrowser& fb, const IViewManager& vm)
|
||||
: m_fb(fb)
|
||||
{
|
||||
m_nameCol = vm.translateOr("name", "Name");
|
||||
m_typeCol = vm.translateOr("type", "Type");
|
||||
|
@ -225,12 +252,30 @@ private:
|
|||
} m_fileListingBind;
|
||||
ViewChild<std::unique_ptr<Table>> m_fileListing;
|
||||
|
||||
struct BookmarkDataBind : ITableDataBinding
|
||||
struct BookmarkDataBind : ITableDataBinding, ITableStateBinding
|
||||
{
|
||||
FileBrowser& m_fb;
|
||||
BookmarkDataBind(FileBrowser& fb) : m_fb(fb) {}
|
||||
|
||||
struct Entry
|
||||
{
|
||||
HECL::SystemString m_path;
|
||||
std::string m_name;
|
||||
Entry(const HECL::SystemString& path)
|
||||
: m_path(path)
|
||||
{
|
||||
HECL::SystemUTF8View utf8(path);
|
||||
if (utf8.str().size() == 1 && utf8.str()[0] == '/')
|
||||
{
|
||||
m_name = "/";
|
||||
return;
|
||||
}
|
||||
size_t lastSlash = utf8.str().rfind('/');
|
||||
if (lastSlash != std::string::npos)
|
||||
m_name.assign(utf8.str().cbegin() + lastSlash + 1, utf8.str().cend());
|
||||
else
|
||||
m_name = utf8.str();
|
||||
}
|
||||
};
|
||||
std::vector<Entry> m_entries;
|
||||
|
||||
|
@ -241,6 +286,12 @@ private:
|
|||
{
|
||||
return &m_entries.at(rIdx).m_name;
|
||||
}
|
||||
|
||||
void setSelectedRow(size_t rIdx)
|
||||
{
|
||||
if (rIdx != -1)
|
||||
m_fb.navigateToPath(m_entries.at(rIdx).m_path);
|
||||
}
|
||||
};
|
||||
|
||||
BookmarkDataBind m_systemBookmarkBind;
|
||||
|
@ -255,12 +306,25 @@ private:
|
|||
std::unique_ptr<TextView> m_recentBookmarksLabel;
|
||||
ViewChild<std::unique_ptr<Table>> 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);
|
||||
std::function<void(bool, const HECL::SystemString&)> m_returnFunc;
|
||||
|
||||
public:
|
||||
FileBrowser(ViewResources& res, View& parentView, const std::string& title, Type type,
|
||||
std::function<void(bool, const HECL::SystemString&)> returnFunc)
|
||||
: FileBrowser(res, parentView, title, type, HECL::GetcwdStr(), returnFunc) {}
|
||||
FileBrowser(ViewResources& res, View& parentView, const std::string& title, Type type,
|
||||
const HECL::SystemString& initialPath,
|
||||
std::function<void(bool, const HECL::SystemString&)> returnFunc);
|
||||
|
||||
static void SyncBookmarkSelections(Table& table, BookmarkDataBind& binding,
|
||||
const HECL::SystemString& sel);
|
||||
void navigateToPath(const HECL::SystemString& path);
|
||||
bool showingHidden() const {return m_showingHidden;}
|
||||
void setShowingHidden(bool showingHidden)
|
||||
{
|
||||
m_showingHidden = showingHidden;
|
||||
navigateToPath(m_path);
|
||||
}
|
||||
void updateContentOpacity(float opacity);
|
||||
|
||||
void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||
|
@ -272,6 +336,8 @@ public:
|
|||
void touchDown(const boo::STouchCoord&, uintptr_t);
|
||||
void touchUp(const boo::STouchCoord&, uintptr_t);
|
||||
void touchMove(const boo::STouchCoord&, uintptr_t);
|
||||
void charKeyDown(unsigned long, boo::EModifierKey, bool);
|
||||
void specialKeyDown(boo::ESpecialKey, boo::EModifierKey, bool);
|
||||
|
||||
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
|
||||
void think();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define SPECTER_IVIEWMANAGER_HPP
|
||||
|
||||
#include "Translator.hpp"
|
||||
#include <HECL/HECL.hpp>
|
||||
|
||||
namespace Specter
|
||||
{
|
||||
|
@ -17,6 +18,12 @@ public:
|
|||
return trans->translateOr(key, vor);
|
||||
return vor;
|
||||
}
|
||||
|
||||
virtual const std::vector<HECL::SystemString>* recentProjects() const {return nullptr;}
|
||||
virtual void pushRecentProject(const HECL::SystemString& path) {}
|
||||
|
||||
virtual const std::vector<HECL::SystemString>* recentFiles() const {return nullptr;}
|
||||
virtual void pushRecentFile(const HECL::SystemString& path) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ struct ITableDataBinding
|
|||
struct ITableStateBinding
|
||||
{
|
||||
virtual float getColumnSplit(size_t cIdx) const {return -1.0;}
|
||||
virtual bool columnSplitResizeAllowed() const {return false;}
|
||||
virtual void setColumnSplit(size_t cIdx, float split) {}
|
||||
virtual SortDirection getSort(size_t& cIdx) const {cIdx = 0; return SortDirection::None;}
|
||||
virtual void setSort(size_t cIdx, SortDirection dir) {}
|
||||
|
@ -50,6 +51,7 @@ class Table : public View
|
|||
Table& m_t;
|
||||
std::unique_ptr<TextView> m_text;
|
||||
size_t m_c, m_r;
|
||||
boo::SWindowRect m_scissorRect;
|
||||
CellView(Table& t, ViewResources& res, size_t c, size_t r);
|
||||
|
||||
bool m_selected = false;
|
||||
|
@ -60,19 +62,25 @@ class Table : public View
|
|||
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||
void mouseEnter(const boo::SWindowCoord&);
|
||||
void mouseLeave(const boo::SWindowCoord&);
|
||||
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
|
||||
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub,
|
||||
const boo::SWindowRect& scissor);
|
||||
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||
};
|
||||
std::vector<ViewChild<std::unique_ptr<CellView>>> m_headerViews;
|
||||
std::vector<std::vector<ViewChild<std::unique_ptr<CellView>>>> m_cellViews;
|
||||
bool m_header = false;
|
||||
|
||||
std::vector<boo::SWindowRect> m_hCellRects;
|
||||
size_t m_hDraggingIdx = 0;
|
||||
|
||||
std::unique_ptr<SolidShaderVert[]> m_hVerts;
|
||||
boo::IGraphicsBufferD* m_hVertsBuf = nullptr;
|
||||
boo::IVertexFormat* m_hVtxFmt = nullptr; /* OpenGL only */
|
||||
boo::IShaderDataBinding* m_hShaderBinding = nullptr;
|
||||
void _setHeaderVerts(const boo::SWindowRect& rect);
|
||||
|
||||
std::vector<boo::SWindowRect> getCellRects(const boo::SWindowRect& tableRect) const;
|
||||
|
||||
ViewChild<std::unique_ptr<ScrollView>> m_scroll;
|
||||
|
||||
struct RowsView : public View
|
||||
|
|
|
@ -16,8 +16,9 @@ class TextField : public ITextInputView
|
|||
std::string m_deferredTextStr;
|
||||
std::string m_deferredMarkStr;
|
||||
std::unique_ptr<TextView> m_text;
|
||||
std::unique_ptr<TextView> m_errText;
|
||||
|
||||
SolidShaderVert m_verts[32];
|
||||
SolidShaderVert m_verts[41];
|
||||
boo::IGraphicsBufferD* m_bVertsBuf = nullptr;
|
||||
boo::IVertexFormat* m_bVtxFmt = nullptr; /* OpenGL only */
|
||||
boo::IShaderDataBinding* m_bShaderBinding = nullptr;
|
||||
|
@ -48,15 +49,24 @@ class TextField : public ITextInputView
|
|||
size_t m_cursorFrames = 0;
|
||||
size_t m_clickFrames = 15;
|
||||
size_t m_clickFrames2 = 15;
|
||||
size_t m_errorFrames = 360;
|
||||
|
||||
size_t m_dragStart = 0;
|
||||
size_t m_dragging = 0;
|
||||
|
||||
bool m_active = false;
|
||||
bool m_error = false;
|
||||
|
||||
enum class BGState
|
||||
{
|
||||
Inactive,
|
||||
Hover,
|
||||
Disabled
|
||||
} m_bgState = BGState::Inactive;
|
||||
void setInactive();
|
||||
void setHover();
|
||||
void setDisabled();
|
||||
void refreshBg();
|
||||
|
||||
public:
|
||||
TextField(ViewResources& res, View& parentView, IStringBinding* strBind);
|
||||
|
@ -99,6 +109,8 @@ public:
|
|||
|
||||
void setActive(bool active);
|
||||
void setCursorPos(size_t pos);
|
||||
void setErrorState(const std::string& message);
|
||||
void clearErrorState();
|
||||
|
||||
void setSelectionRange(size_t start, size_t count);
|
||||
void clearSelectionRange();
|
||||
|
@ -109,8 +121,10 @@ public:
|
|||
m_viewVertBlock.m_color = color;
|
||||
m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(ViewBlock));
|
||||
m_text->setMultiplyColor(color);
|
||||
if (m_errText)
|
||||
m_errText->setMultiplyColor(color);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
void _setCursorPos();
|
||||
void _reallySetCursorPos(size_t pos);
|
||||
|
|
|
@ -89,7 +89,7 @@ public:
|
|||
void colorGlyphsTypeOn(const Zeus::CColor& newColor, float startInterval=0.2, float fadeTime=0.5);
|
||||
void think();
|
||||
|
||||
void resized(const boo::SWindowRect &rootView, const boo::SWindowRect& sub);
|
||||
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
|
||||
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||
|
||||
int nominalWidth() const {return m_width;}
|
||||
|
|
|
@ -19,7 +19,7 @@ class ThemeData
|
|||
|
||||
Zeus::CColor m_vpBg = {0.2, 0.2, 0.2, 1.0};
|
||||
Zeus::CColor m_tbBg = {0.4, 0.4, 0.4, 1.0};
|
||||
Zeus::CColor m_tooltipBg = {0.0, 0.0, 0.0, 0.65};
|
||||
Zeus::CColor m_tooltipBg = {0.1, 0.1, 0.1, 0.85};
|
||||
Zeus::CColor m_splashBg = {0.075, 0.075, 0.075, 0.85};
|
||||
|
||||
Zeus::CColor m_splash1 = {1.0, 1.0, 1.0, 1.0};
|
||||
|
|
|
@ -276,6 +276,8 @@ void Button::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
|||
if (m_style == Style::Block)
|
||||
textRect.location[1] += 7 * pf;
|
||||
textRect.location[0] += sub.size[0] / 2;
|
||||
textRect.size[0] = m_nomWidth;
|
||||
textRect.size[1] = m_nomHeight;
|
||||
m_text->resized(root, textRect);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
namespace Specter
|
||||
{
|
||||
static LogVisor::LogModule Log("Specter::FileBrowser");
|
||||
|
||||
#define BROWSER_MARGIN 8
|
||||
#define BROWSER_MIN_WIDTH 600
|
||||
|
@ -40,7 +41,8 @@ static std::vector<HECL::SystemString> PathComponents(const HECL::SystemString&
|
|||
}
|
||||
|
||||
FileBrowser::FileBrowser(ViewResources& res, View& parentView, const std::string& title,
|
||||
Type type, const HECL::SystemString& initialPath)
|
||||
Type type, const HECL::SystemString& initialPath,
|
||||
std::function<void(bool, const HECL::SystemString&)> returnFunc)
|
||||
: ModalWindow(res, parentView, RectangleConstraint(BROWSER_MIN_WIDTH * res.pixelFactor(),
|
||||
BROWSER_MIN_HEIGHT * res.pixelFactor(),
|
||||
RectangleConstraint::Test::Minimum,
|
||||
|
@ -51,7 +53,11 @@ FileBrowser::FileBrowser(ViewResources& res, View& parentView, const std::string
|
|||
m_ok(*this, res, title),
|
||||
m_cancel(*this, res, rootView().viewManager().translateOr("cancel", "Cancel")),
|
||||
m_fileFieldBind(*this, rootView().viewManager()),
|
||||
m_fileListingBind(rootView().viewManager())
|
||||
m_fileListingBind(*this, rootView().viewManager()),
|
||||
m_systemBookmarkBind(*this),
|
||||
m_projectBookmarkBind(*this),
|
||||
m_recentBookmarkBind(*this),
|
||||
m_returnFunc(returnFunc)
|
||||
{
|
||||
commitResources(res);
|
||||
setBackground({0,0,0,0.5});
|
||||
|
@ -59,16 +65,22 @@ FileBrowser::FileBrowser(ViewResources& res, View& parentView, const std::string
|
|||
IViewManager& vm = rootView().viewManager();
|
||||
m_fileField.m_view.reset(new TextField(res, *this, &m_fileFieldBind));
|
||||
m_fileListing.m_view.reset(new Table(res, *this, &m_fileListingBind, &m_fileListingBind, 3));
|
||||
m_systemBookmarks.m_view.reset(new Table(res, *this, &m_systemBookmarkBind, nullptr, 1));
|
||||
m_systemBookmarks.m_view.reset(new Table(res, *this, &m_systemBookmarkBind, &m_systemBookmarkBind, 1));
|
||||
m_systemBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont));
|
||||
m_systemBookmarksLabel->typesetGlyphs(vm.translateOr("system_locations", "System Locations"), res.themeData().uiText());
|
||||
m_projectBookmarks.m_view.reset(new Table(res, *this, &m_projectBookmarkBind, nullptr, 1));
|
||||
m_projectBookmarks.m_view.reset(new Table(res, *this, &m_projectBookmarkBind, &m_projectBookmarkBind, 1));
|
||||
m_projectBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont));
|
||||
m_projectBookmarksLabel->typesetGlyphs(vm.translateOr("recent_projects", "Recent Projects"), res.themeData().uiText());
|
||||
m_recentBookmarks.m_view.reset(new Table(res, *this, &m_recentBookmarkBind, nullptr, 1));
|
||||
m_recentBookmarks.m_view.reset(new Table(res, *this, &m_recentBookmarkBind, &m_recentBookmarkBind, 1));
|
||||
m_recentBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont));
|
||||
m_recentBookmarksLabel->typesetGlyphs(vm.translateOr("recent_files", "Recent Files"), res.themeData().uiText());
|
||||
|
||||
/* Populate system bookmarks */
|
||||
std::vector<HECL::SystemString> systemLocs = HECL::GetSystemLocations();
|
||||
for (const HECL::SystemString& loc : systemLocs)
|
||||
m_systemBookmarkBind.m_entries.emplace_back(loc);
|
||||
m_systemBookmarks.m_view->updateData();
|
||||
|
||||
navigateToPath(initialPath);
|
||||
|
||||
m_split.m_view.reset(new SplitView(res, *this, SplitView::Axis::Vertical,
|
||||
|
@ -80,6 +92,23 @@ FileBrowser::FileBrowser(ViewResources& res, View& parentView, const std::string
|
|||
updateContentOpacity(0.0);
|
||||
}
|
||||
|
||||
void FileBrowser::SyncBookmarkSelections(Table& table, BookmarkDataBind& binding,
|
||||
const HECL::SystemString& sel)
|
||||
{
|
||||
size_t idx = 0;
|
||||
for (const BookmarkDataBind::Entry& e : binding.m_entries)
|
||||
{
|
||||
if (e.m_path == sel)
|
||||
{
|
||||
table.selectRow(idx);
|
||||
break;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
if (idx == binding.m_entries.size())
|
||||
table.selectRow(-1);
|
||||
}
|
||||
|
||||
void FileBrowser::navigateToPath(const HECL::SystemString& path)
|
||||
{
|
||||
HECL::Sstat theStat;
|
||||
|
@ -92,6 +121,7 @@ void FileBrowser::navigateToPath(const HECL::SystemString& path)
|
|||
{
|
||||
HECL::SystemUTF8View utf8(m_comps.back());
|
||||
m_fileField.m_view->setText(utf8);
|
||||
m_fileField.m_view->clearErrorState();
|
||||
m_comps.pop_back();
|
||||
}
|
||||
|
||||
|
@ -100,12 +130,20 @@ void FileBrowser::navigateToPath(const HECL::SystemString& path)
|
|||
for (const HECL::SystemString& d : m_comps)
|
||||
{
|
||||
if (needSlash)
|
||||
dir += '/';
|
||||
needSlash = true;
|
||||
dir += _S('/');
|
||||
if (d.compare(_S("/")))
|
||||
needSlash = true;
|
||||
dir += d;
|
||||
}
|
||||
|
||||
SyncBookmarkSelections(*m_systemBookmarks.m_view, m_systemBookmarkBind, dir);
|
||||
SyncBookmarkSelections(*m_projectBookmarks.m_view, m_projectBookmarkBind, dir);
|
||||
SyncBookmarkSelections(*m_recentBookmarks.m_view, m_recentBookmarkBind, dir);
|
||||
|
||||
HECL::DirectoryEnumerator dEnum(dir, HECL::DirectoryEnumerator::Mode::DirsThenFilesSorted,
|
||||
m_fileListingBind.m_sizeSort, m_fileListingBind.m_sortDir==SortDirection::Descending);
|
||||
m_fileListingBind.m_sizeSort,
|
||||
m_fileListingBind.m_sortDir==SortDirection::Descending,
|
||||
!m_showingHidden);
|
||||
m_fileListingBind.updateListing(dEnum);
|
||||
m_fileListing.m_view->selectRow(-1);
|
||||
m_fileListing.m_view->updateData();
|
||||
|
@ -138,12 +176,104 @@ void FileBrowser::updateContentOpacity(float opacity)
|
|||
m_recentBookmarksLabel->setMultiplyColor(color);
|
||||
}
|
||||
|
||||
void FileBrowser::okActivated()
|
||||
void FileBrowser::okActivated(bool viaButton)
|
||||
{
|
||||
IViewManager& vm = rootView().viewManager();
|
||||
|
||||
HECL::SystemString path;
|
||||
bool needSlash = false;
|
||||
for (const HECL::SystemString& d : m_comps)
|
||||
{
|
||||
if (needSlash)
|
||||
path += _S('/');
|
||||
if (d.compare(_S("/")))
|
||||
needSlash = true;
|
||||
path += d;
|
||||
}
|
||||
|
||||
HECL::Sstat theStat;
|
||||
if (HECL::Stat(path.c_str(), &theStat) || !S_ISDIR(theStat.st_mode))
|
||||
{
|
||||
HECL::SystemUTF8View utf8(path);
|
||||
m_fileField.m_view->setErrorState(
|
||||
HECL::Format(vm.translateOr("no_access_as_dir", "Unable to access '%s' as directory").c_str(),
|
||||
utf8.c_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
path += _S('/');
|
||||
path += m_fileField.m_view->getText();
|
||||
|
||||
int err = HECL::Stat(path.c_str(), &theStat);
|
||||
if (m_type == Type::SaveFile)
|
||||
{
|
||||
if (!err && S_ISDIR(theStat.st_mode))
|
||||
{
|
||||
navigateToPath(path);
|
||||
return;
|
||||
}
|
||||
m_returnFunc(true, path);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_type == Type::OpenFile)
|
||||
{
|
||||
if (!err && S_ISDIR(theStat.st_mode))
|
||||
{
|
||||
navigateToPath(path);
|
||||
return;
|
||||
}
|
||||
else if (err || !S_ISREG(theStat.st_mode))
|
||||
{
|
||||
HECL::SystemUTF8View utf8(path);
|
||||
m_fileField.m_view->setErrorState(
|
||||
HECL::Format(vm.translateOr("no_access_as_file", "Unable to access '%s' as file").c_str(),
|
||||
utf8.c_str()));
|
||||
return;
|
||||
}
|
||||
m_returnFunc(true, path);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
else if (m_type == Type::OpenDirectory || m_type == Type::OpenHECLProject)
|
||||
{
|
||||
if (!viaButton && !err && S_ISDIR(theStat.st_mode))
|
||||
{
|
||||
navigateToPath(path);
|
||||
return;
|
||||
}
|
||||
if (err || !S_ISDIR(theStat.st_mode))
|
||||
{
|
||||
HECL::SystemUTF8View utf8(path);
|
||||
m_fileField.m_view->setErrorState(
|
||||
HECL::Format(vm.translateOr("no_access_as_dir", "Unable to access '%s' as directory").c_str(),
|
||||
utf8.c_str()));
|
||||
return;
|
||||
}
|
||||
m_returnFunc(true, path);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void FileBrowser::cancelActivated()
|
||||
{
|
||||
HECL::SystemString path;
|
||||
bool needSlash = false;
|
||||
for (const HECL::SystemString& d : m_comps)
|
||||
{
|
||||
if (needSlash)
|
||||
path += _S('/');
|
||||
if (d.compare(_S("/")))
|
||||
needSlash = true;
|
||||
path += d;
|
||||
}
|
||||
|
||||
path += _S('/');
|
||||
path += m_fileField.m_view->getText();
|
||||
|
||||
m_returnFunc(false, path);
|
||||
close();
|
||||
}
|
||||
|
||||
|
@ -158,8 +288,9 @@ void FileBrowser::pathButtonActivated(size_t idx)
|
|||
for (const HECL::SystemString& d : m_comps)
|
||||
{
|
||||
if (needSlash)
|
||||
dir += '/';
|
||||
needSlash = true;
|
||||
dir += _S('/');
|
||||
if (d.compare(_S("/")))
|
||||
needSlash = true;
|
||||
dir += d;
|
||||
if (++i > idx)
|
||||
break;
|
||||
|
@ -176,6 +307,9 @@ void FileBrowser::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton bu
|
|||
b.m_button.mouseDown(coord, button, mod);
|
||||
m_fileField.mouseDown(coord, button, mod);
|
||||
m_fileListing.mouseDown(coord, button, mod);
|
||||
m_systemBookmarks.m_view->mouseDown(coord, button, mod);
|
||||
m_projectBookmarks.m_view->mouseDown(coord, button, mod);
|
||||
m_recentBookmarks.m_view->mouseDown(coord, button, mod);
|
||||
m_ok.m_button.mouseDown(coord, button, mod);
|
||||
m_cancel.m_button.mouseDown(coord, button, mod);
|
||||
}
|
||||
|
@ -197,6 +331,9 @@ void FileBrowser::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton butt
|
|||
|
||||
m_fileField.mouseUp(coord, button, mod);
|
||||
m_fileListing.mouseUp(coord, button, mod);
|
||||
m_systemBookmarks.m_view->mouseUp(coord, button, mod);
|
||||
m_projectBookmarks.m_view->mouseUp(coord, button, mod);
|
||||
m_recentBookmarks.m_view->mouseUp(coord, button, mod);
|
||||
m_ok.m_button.mouseUp(coord, button, mod);
|
||||
m_cancel.m_button.mouseUp(coord, button, mod);
|
||||
}
|
||||
|
@ -210,6 +347,9 @@ void FileBrowser::mouseMove(const boo::SWindowCoord& coord)
|
|||
b.m_button.mouseMove(coord);
|
||||
m_fileField.mouseMove(coord);
|
||||
m_fileListing.mouseMove(coord);
|
||||
m_systemBookmarks.m_view->mouseMove(coord);
|
||||
m_projectBookmarks.m_view->mouseMove(coord);
|
||||
m_recentBookmarks.m_view->mouseMove(coord);
|
||||
m_ok.m_button.mouseMove(coord);
|
||||
m_cancel.m_button.mouseMove(coord);
|
||||
}
|
||||
|
@ -248,6 +388,27 @@ void FileBrowser::touchMove(const boo::STouchCoord& coord, uintptr_t tid)
|
|||
{
|
||||
}
|
||||
|
||||
void FileBrowser::charKeyDown(unsigned long charcode, boo::EModifierKey mod, bool isRepeat)
|
||||
{
|
||||
if (skipBuildInAnimation() || closed())
|
||||
return;
|
||||
if ((mod & boo::EModifierKey::CtrlCommand) != boo::EModifierKey::None && !isRepeat)
|
||||
{
|
||||
if (charcode == 'h' || charcode == 'H')
|
||||
setShowingHidden(!showingHidden());
|
||||
else if (charcode == 'r' || charcode == 'R')
|
||||
navigateToPath(m_path);
|
||||
}
|
||||
}
|
||||
|
||||
void FileBrowser::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mod, bool isRepeat)
|
||||
{
|
||||
if (skipBuildInAnimation() || closed())
|
||||
return;
|
||||
if (key == boo::ESpecialKey::Enter && !isRepeat)
|
||||
okActivated(true);
|
||||
}
|
||||
|
||||
void FileBrowser::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
||||
{
|
||||
ModalWindow::resized(root, root);
|
||||
|
@ -361,10 +522,10 @@ void FileBrowser::RightSide::draw(boo::IGraphicsCommandQueue* gfxQ)
|
|||
{
|
||||
for (PathButton& b : m_fb.m_pathButtons)
|
||||
b.m_button.m_view->draw(gfxQ);
|
||||
m_fb.m_fileField.m_view->draw(gfxQ);
|
||||
m_fb.m_fileListing.m_view->draw(gfxQ);
|
||||
m_fb.m_ok.m_button.m_view->draw(gfxQ);
|
||||
m_fb.m_cancel.m_button.m_view->draw(gfxQ);
|
||||
m_fb.m_fileField.m_view->draw(gfxQ);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,10 +50,10 @@ void RootView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button,
|
|||
|
||||
void RootView::mouseMove(const boo::SWindowCoord& coord)
|
||||
{
|
||||
if (m_view)
|
||||
m_view->mouseMove(coord);
|
||||
if (m_activeDragView)
|
||||
m_activeDragView->mouseMove(coord);
|
||||
else if (m_view)
|
||||
m_view->mouseMove(coord);
|
||||
|
||||
boo::SWindowRect ttrect = m_rootRect;
|
||||
ttrect.location[0] = coord.pixel[0];
|
||||
|
|
|
@ -8,14 +8,17 @@ static LogVisor::LogModule Log("Specter::Table");
|
|||
#define ROW_HEIGHT 18
|
||||
#define CELL_MARGIN 1
|
||||
|
||||
Table::Table(ViewResources& res, View& parentView, ITableDataBinding* data, ITableStateBinding* state, size_t maxColumns)
|
||||
Table::Table(ViewResources& res, View& parentView, ITableDataBinding* data,
|
||||
ITableStateBinding* state, size_t maxColumns)
|
||||
: View(res, parentView), m_data(data), m_state(state), m_maxColumns(maxColumns),
|
||||
m_hVerts(new SolidShaderVert[maxColumns * 6]), m_rowsView(*this, res)
|
||||
{
|
||||
if (!maxColumns)
|
||||
Log.report(LogVisor::FatalError, "0-column tables not supported");
|
||||
|
||||
m_hVertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(SolidShaderVert), maxColumns * 6);
|
||||
m_hVertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex,
|
||||
sizeof(SolidShaderVert),
|
||||
maxColumns * 6);
|
||||
|
||||
if (!res.m_viewRes.m_texVtxFmt)
|
||||
{
|
||||
|
@ -86,7 +89,6 @@ void Table::_setHeaderVerts(const boo::SWindowRect& sub)
|
|||
const ThemeData& theme = rootView().themeData();
|
||||
|
||||
float pf = rootView().viewRes().pixelFactor();
|
||||
int div = sub.size[0] / m_headerViews.size();
|
||||
int margin = CELL_MARGIN * pf;
|
||||
int rowHeight = ROW_HEIGHT * pf;
|
||||
int xOff = 0;
|
||||
|
@ -99,6 +101,12 @@ void Table::_setHeaderVerts(const boo::SWindowRect& sub)
|
|||
if (m_state)
|
||||
sDir = m_state->getSort(sCol);
|
||||
|
||||
boo::SWindowRect headRowRect = sub;
|
||||
headRowRect.size[1] = rowHeight;
|
||||
headRowRect.location[1] = sub.size[1] - rowHeight;
|
||||
m_hCellRects = getCellRects(headRowRect);
|
||||
auto cellRectsIt = m_hCellRects.begin();
|
||||
|
||||
for (c=0 ; c<std::min(m_maxColumns, m_columns) ; ++c)
|
||||
{
|
||||
const ViewChild<std::unique_ptr<CellView>>& hv = *it;
|
||||
|
@ -131,6 +139,7 @@ void Table::_setHeaderVerts(const boo::SWindowRect& sub)
|
|||
}
|
||||
}
|
||||
|
||||
int div = cellRectsIt->size[0];
|
||||
v[0].m_pos.assign(xOff + margin, yOff - margin, 0);
|
||||
v[0].m_color = cm1;
|
||||
v[1] = v[0];
|
||||
|
@ -144,6 +153,7 @@ void Table::_setHeaderVerts(const boo::SWindowRect& sub)
|
|||
v += 6;
|
||||
xOff += div;
|
||||
++it;
|
||||
++cellRectsIt;
|
||||
}
|
||||
|
||||
if (c)
|
||||
|
@ -161,7 +171,6 @@ void Table::RowsView::_setRowVerts(const boo::SWindowRect& sub, const boo::SWind
|
|||
return;
|
||||
|
||||
float pf = rootView().viewRes().pixelFactor();
|
||||
int div = sub.size[0] / m_t.m_cellViews.size();
|
||||
int spacing = (ROW_HEIGHT + CELL_MARGIN * 2) * pf;
|
||||
int margin = CELL_MARGIN * pf;
|
||||
int rowHeight = ROW_HEIGHT * pf;
|
||||
|
@ -173,15 +182,22 @@ void Table::RowsView::_setRowVerts(const boo::SWindowRect& sub, const boo::SWind
|
|||
++idx;
|
||||
}
|
||||
int startIdx = int(m_t.m_rows) - idx;
|
||||
if (!m_t.m_header)
|
||||
startIdx -= 1;
|
||||
|
||||
std::vector<boo::SWindowRect> cellRects = m_t.getCellRects(sub);
|
||||
|
||||
size_t r, c;
|
||||
for (r=0, c=0 ; r<SPECTER_TABLE_MAX_ROWS && (sub.location[1] + yOff + spacing) >= scissor.location[1] ; ++r)
|
||||
for (r=0, c=0 ; r<SPECTER_TABLE_MAX_ROWS &&
|
||||
(sub.location[1] + yOff + spacing) >= scissor.location[1] ; ++r)
|
||||
{
|
||||
const Zeus::CColor& color = (startIdx+r==m_t.m_selectedRow) ? theme.tableCellBgSelected() :
|
||||
((r&1) ? theme.tableCellBg1() : theme.tableCellBg2());
|
||||
int xOff = 0;
|
||||
auto cellRectsIt = cellRects.begin();
|
||||
for (c=0 ; c<std::min(m_t.m_maxColumns, m_t.m_columns) ; ++c)
|
||||
{
|
||||
int div = cellRectsIt->size[0];
|
||||
v[0].m_pos.assign(xOff + margin, yOff - margin, 0);
|
||||
v[0].m_color = color;
|
||||
v[1] = v[0];
|
||||
|
@ -194,6 +210,7 @@ void Table::RowsView::_setRowVerts(const boo::SWindowRect& sub, const boo::SWind
|
|||
v[5] = v[4];
|
||||
v += 6;
|
||||
xOff += div;
|
||||
++cellRectsIt;
|
||||
}
|
||||
yOff -= spacing;
|
||||
}
|
||||
|
@ -276,15 +293,38 @@ void Table::CellView::deselect()
|
|||
m_text->colorGlyphs(rootView().themeData().uiText());
|
||||
}
|
||||
|
||||
void Table::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||
void Table::mouseDown(const boo::SWindowCoord& coord,
|
||||
boo::EMouseButton button, boo::EModifierKey mod)
|
||||
{
|
||||
if (m_state && m_state->columnSplitResizeAllowed())
|
||||
{
|
||||
size_t cIdx = 0;
|
||||
for (const boo::SWindowRect& rect : m_hCellRects)
|
||||
{
|
||||
if (cIdx == 0)
|
||||
{
|
||||
++cIdx;
|
||||
continue;
|
||||
}
|
||||
if (abs(coord.pixel[0] - rect.location[0]) < 4 &&
|
||||
unsigned(coord.pixel[1] - subRect().location[1] -
|
||||
subRect().size[1] + rect.size[1]) < rect.size[1])
|
||||
{
|
||||
m_hDraggingIdx = cIdx;
|
||||
rootView().setActiveDragView(this);
|
||||
return;
|
||||
}
|
||||
++cIdx;
|
||||
}
|
||||
}
|
||||
|
||||
m_scroll.mouseDown(coord, button, mod);
|
||||
if (m_headerNeedsUpdate)
|
||||
_setHeaderVerts(subRect());
|
||||
}
|
||||
|
||||
|
||||
void Table::RowsView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||
void Table::RowsView::mouseDown(const boo::SWindowCoord& coord,
|
||||
boo::EMouseButton button, boo::EModifierKey mod)
|
||||
{
|
||||
for (ViewChild<std::unique_ptr<CellView>>& hv : m_t.m_headerViews)
|
||||
if (hv.mouseDown(coord, button, mod))
|
||||
|
@ -294,7 +334,8 @@ void Table::RowsView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButto
|
|||
cv.mouseDown(coord, button, mod);
|
||||
}
|
||||
|
||||
void Table::CellView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||
void Table::CellView::mouseDown(const boo::SWindowCoord& coord,
|
||||
boo::EMouseButton button, boo::EModifierKey mod)
|
||||
{
|
||||
if (m_r != -1)
|
||||
{
|
||||
|
@ -308,14 +349,21 @@ void Table::CellView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButto
|
|||
m_t.m_headerNeedsUpdate = true;
|
||||
}
|
||||
|
||||
void Table::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||
void Table::mouseUp(const boo::SWindowCoord& coord,
|
||||
boo::EMouseButton button, boo::EModifierKey mod)
|
||||
{
|
||||
m_scroll.mouseUp(coord, button, mod);
|
||||
if (m_headerNeedsUpdate)
|
||||
_setHeaderVerts(subRect());
|
||||
if (m_hDraggingIdx)
|
||||
{
|
||||
rootView().setActiveDragView(nullptr);
|
||||
m_hDraggingIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Table::RowsView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||
void Table::RowsView::mouseUp(const boo::SWindowCoord& coord,
|
||||
boo::EMouseButton button, boo::EModifierKey mod)
|
||||
{
|
||||
size_t idx = 0;
|
||||
for (ViewChild<std::unique_ptr<CellView>>& hv : m_t.m_headerViews)
|
||||
|
@ -330,7 +378,8 @@ void Table::RowsView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton
|
|||
cv.mouseUp(coord, button, mod);
|
||||
}
|
||||
|
||||
void Table::CellView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||
void Table::CellView::mouseUp(const boo::SWindowCoord& coord,
|
||||
boo::EMouseButton button, boo::EModifierKey mod)
|
||||
{
|
||||
if (m_r == -1)
|
||||
m_t.m_headerNeedsUpdate = true;
|
||||
|
@ -338,6 +387,43 @@ void Table::CellView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton
|
|||
|
||||
void Table::mouseMove(const boo::SWindowCoord& coord)
|
||||
{
|
||||
if (m_state && m_state->columnSplitResizeAllowed())
|
||||
{
|
||||
if (m_hDraggingIdx)
|
||||
{
|
||||
float split = (coord.pixel[0] - subRect().location[0]) / float(subRect().size[0]);
|
||||
|
||||
if (m_hDraggingIdx <= 1)
|
||||
split = std::max(0.03f, split);
|
||||
else
|
||||
split = std::max(m_state->getColumnSplit(m_hDraggingIdx-1)+0.03f, split);
|
||||
|
||||
if (m_hDraggingIdx >= m_columns - 1)
|
||||
split = std::min(0.97f, split);
|
||||
else
|
||||
split = std::min(m_state->getColumnSplit(m_hDraggingIdx+1)-0.03f, split);
|
||||
|
||||
m_state->setColumnSplit(m_hDraggingIdx, split);
|
||||
updateSize();
|
||||
return;
|
||||
}
|
||||
size_t cIdx = 0;
|
||||
bool hovering = false;
|
||||
for (const boo::SWindowRect& rect : m_hCellRects)
|
||||
{
|
||||
if (cIdx++ == 0)
|
||||
continue;
|
||||
if (abs(coord.pixel[0] - rect.location[0]) < 4 &&
|
||||
unsigned(coord.pixel[1] - subRect().location[1] -
|
||||
subRect().size[1] + rect.size[1]) < rect.size[1])
|
||||
{
|
||||
hovering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rootView().setVerticalSplitHover(hovering);
|
||||
}
|
||||
|
||||
m_scroll.mouseMove(coord);
|
||||
if (m_headerNeedsUpdate)
|
||||
_setHeaderVerts(subRect());
|
||||
|
@ -460,25 +546,77 @@ void Table::updateData()
|
|||
updateSize();
|
||||
}
|
||||
|
||||
std::vector<boo::SWindowRect> Table::getCellRects(const boo::SWindowRect& sub) const
|
||||
{
|
||||
if (!m_columns)
|
||||
return {};
|
||||
float pf = rootView().viewRes().pixelFactor();
|
||||
|
||||
/* Validate column split values */
|
||||
bool valid = false;
|
||||
std::vector<float> splits;
|
||||
splits.reserve(m_columns);
|
||||
if (m_state)
|
||||
{
|
||||
float lastSplit = 0.0;
|
||||
size_t i;
|
||||
for (i=0 ; i<m_columns ; ++i)
|
||||
{
|
||||
float split = m_state->getColumnSplit(i);
|
||||
if (split < lastSplit || split < 0.0 || split > 1.0)
|
||||
break;
|
||||
splits.push_back(split);
|
||||
lastSplit = split;
|
||||
}
|
||||
if (i == m_columns)
|
||||
valid = true;
|
||||
}
|
||||
|
||||
/* Uniform split otherwise */
|
||||
if (!valid)
|
||||
{
|
||||
float split = 0.0;
|
||||
for (size_t i=0 ; i<m_columns ; ++i)
|
||||
{
|
||||
splits.push_back(split);
|
||||
split += 1.0 / float(m_columns);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<boo::SWindowRect> ret;
|
||||
ret.reserve(m_columns);
|
||||
|
||||
int lastX = 0;
|
||||
for (size_t i=0 ; i<m_columns ; ++i)
|
||||
{
|
||||
float nextSplit = (i==m_columns-1) ? 1.0 : splits[i+1];
|
||||
int x = nextSplit * sub.size[0];
|
||||
ret.push_back({{sub.location[0] + lastX, sub.location[1]},
|
||||
{x - lastX, int(ROW_HEIGHT * pf)}});
|
||||
lastX = x;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void Table::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
||||
{
|
||||
View::resized(root, sub);
|
||||
if (m_scroll.m_view)
|
||||
m_scroll.m_view->resized(root, sub);
|
||||
|
||||
boo::SWindowRect headRow = sub;
|
||||
float pf = rootView().viewRes().pixelFactor();
|
||||
boo::SWindowRect cell = sub;
|
||||
cell.size[1] = ROW_HEIGHT * pf;
|
||||
cell.location[1] += sub.size[1] - cell.size[1];
|
||||
int div = sub.size[0] / m_cellViews.size();
|
||||
cell.size[0] = div;
|
||||
|
||||
headRow.location[1] += sub.size[1] - ROW_HEIGHT * pf;
|
||||
std::vector<boo::SWindowRect> cellRects = getCellRects(headRow);
|
||||
_setHeaderVerts(sub);
|
||||
size_t cIdx = 0;
|
||||
for (ViewChild<std::unique_ptr<CellView>>& hv : m_headerViews)
|
||||
{
|
||||
if (hv.m_view)
|
||||
hv.m_view->resized(root, cell);
|
||||
cell.location[0] += div;
|
||||
hv.m_view->resized(root, cellRects[cIdx], sub);
|
||||
++cIdx;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,34 +635,32 @@ void Table::RowsView::resized(const boo::SWindowRect& root, const boo::SWindowRe
|
|||
View::resized(root, sub);
|
||||
_setRowVerts(sub, scissor);
|
||||
|
||||
if (m_t.m_cellViews.empty())
|
||||
if (!m_t.m_columns)
|
||||
return;
|
||||
|
||||
float pf = rootView().viewRes().pixelFactor();
|
||||
int div = sub.size[0] / m_t.m_cellViews.size();
|
||||
boo::SWindowRect cell = sub;
|
||||
cell.size[0] = div;
|
||||
cell.size[1] = ROW_HEIGHT * pf;
|
||||
cell.location[1] += sub.size[1] - cell.size[1];
|
||||
boo::SWindowRect rowRect = sub;
|
||||
rowRect.location[1] += sub.size[1] - ROW_HEIGHT * pf;
|
||||
int spacing = (ROW_HEIGHT + CELL_MARGIN * 2) * pf;
|
||||
int hStart = cell.location[1];
|
||||
std::vector<boo::SWindowRect> cellRects = m_t.getCellRects(rowRect);
|
||||
auto cellRectIt = cellRects.begin();
|
||||
for (auto& col : m_t.m_cellViews)
|
||||
{
|
||||
cell.location[1] = hStart;
|
||||
for (ViewChild<std::unique_ptr<CellView>>& cv : col)
|
||||
{
|
||||
cell.location[1] -= spacing;
|
||||
cellRectIt->location[1] -= spacing;
|
||||
if (cv.m_view)
|
||||
cv.m_view->resized(root, cell);
|
||||
cv.m_view->resized(root, *cellRectIt, scissor);
|
||||
}
|
||||
cell.location[0] += div;
|
||||
++cellRectIt;
|
||||
}
|
||||
|
||||
m_scissorRect = scissor;
|
||||
m_scissorRect.size[1] -= spacing;
|
||||
}
|
||||
|
||||
void Table::CellView::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
||||
void Table::CellView::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub,
|
||||
const boo::SWindowRect& scissor)
|
||||
{
|
||||
View::resized(root, sub);
|
||||
boo::SWindowRect textRect = sub;
|
||||
|
@ -532,6 +668,7 @@ void Table::CellView::resized(const boo::SWindowRect& root, const boo::SWindowRe
|
|||
textRect.location[0] += 5 * pf;
|
||||
textRect.location[1] += 5 * pf;
|
||||
m_text->resized(root, textRect);
|
||||
m_scissorRect = sub.intersect(scissor);
|
||||
}
|
||||
|
||||
void Table::draw(boo::IGraphicsCommandQueue* gfxQ)
|
||||
|
@ -557,22 +694,28 @@ void Table::RowsView::draw(boo::IGraphicsCommandQueue* gfxQ)
|
|||
++idx;
|
||||
}
|
||||
}
|
||||
gfxQ->setScissor(rootView().subRect());
|
||||
|
||||
if (m_t.m_header)
|
||||
{
|
||||
gfxQ->setShaderDataBinding(m_t.m_hShaderBinding);
|
||||
gfxQ->setDrawPrimitive(boo::Primitive::TriStrips);
|
||||
gfxQ->setScissor(rootView().subRect());
|
||||
gfxQ->draw(1, m_t.m_columns * 6 - 2);
|
||||
for (ViewChild<std::unique_ptr<CellView>>& hv : m_t.m_headerViews)
|
||||
if (hv.m_view)
|
||||
hv.m_view->draw(gfxQ);
|
||||
}
|
||||
|
||||
gfxQ->setScissor(rootView().subRect());
|
||||
}
|
||||
|
||||
void Table::CellView::draw(boo::IGraphicsCommandQueue* gfxQ)
|
||||
{
|
||||
m_text->draw(gfxQ);
|
||||
if (m_scissorRect.size[0] && m_scissorRect.size[1])
|
||||
{
|
||||
gfxQ->setScissor(m_scissorRect);
|
||||
m_text->draw(gfxQ);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Specter
|
|||
TextField::TextField(ViewResources& res, View& parentView, IStringBinding* strBind)
|
||||
: ITextInputView(res, parentView, strBind)
|
||||
{
|
||||
m_bVertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(SolidShaderVert), 32);
|
||||
m_bVertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(SolidShaderVert), 41);
|
||||
|
||||
if (!res.m_viewRes.m_texVtxFmt)
|
||||
{
|
||||
|
@ -33,18 +33,14 @@ TextField::TextField(ViewResources& res, View& parentView, IStringBinding* strBi
|
|||
}
|
||||
commitResources(res);
|
||||
|
||||
m_verts[0].m_color = rootView().themeData().textfield1Inactive();
|
||||
m_verts[1].m_color = rootView().themeData().textfield2Inactive();
|
||||
m_verts[2].m_color = rootView().themeData().textfield1Inactive();
|
||||
m_verts[3].m_color = rootView().themeData().textfield2Inactive();
|
||||
m_verts[4].m_color = rootView().themeData().textfield2Inactive();
|
||||
for (int i=5 ; i<28 ; ++i)
|
||||
m_verts[i].m_color = res.themeData().textfield2Inactive();
|
||||
for (int i=28 ; i<32 ; ++i)
|
||||
m_verts[i].m_color = res.themeData().textfieldSelection();
|
||||
setInactive();
|
||||
m_bVertsBuf->load(m_verts, sizeof(m_verts));
|
||||
|
||||
m_text.reset(new TextView(res, *this, res.m_mainFont, TextView::Alignment::Left, 1024));
|
||||
if (strBind)
|
||||
setText(strBind->getDefault());
|
||||
}
|
||||
|
||||
void TextField::_setText()
|
||||
|
@ -53,7 +49,10 @@ void TextField::_setText()
|
|||
{
|
||||
_clearSelectionRange();
|
||||
m_textStr = m_deferredTextStr;
|
||||
m_text->typesetGlyphs(m_textStr, rootView().themeData().fieldText());
|
||||
m_text->typesetGlyphs(m_textStr, m_error ? rootView().themeData().uiText() :
|
||||
rootView().themeData().fieldText());
|
||||
if (m_controlBinding && dynamic_cast<IStringBinding*>(m_controlBinding))
|
||||
static_cast<IStringBinding&>(*m_controlBinding).changed(m_textStr);
|
||||
m_hasTextSet = false;
|
||||
if (m_deferredMarkStr.size())
|
||||
m_hasMarkSet = true;
|
||||
|
@ -94,7 +93,8 @@ void TextField::_setMarkedText()
|
|||
std::string compStr(m_textStr.cbegin(), (UTF8Iterator(m_textStr.cbegin()) + repPoint).iter());
|
||||
compStr += m_deferredMarkStr;
|
||||
compStr += std::string((UTF8Iterator(m_textStr.cbegin()) + repEnd).iter(), m_textStr.cend());
|
||||
m_text->typesetGlyphs(compStr, rootView().themeData().fieldText());
|
||||
m_text->typesetGlyphs(compStr, m_error ? rootView().themeData().uiText() :
|
||||
rootView().themeData().fieldText());
|
||||
|
||||
size_t pos = m_cursorPos;
|
||||
if (m_deferredMarkStr.size())
|
||||
|
@ -128,32 +128,96 @@ void TextField::setText(const std::string& str)
|
|||
|
||||
void TextField::setInactive()
|
||||
{
|
||||
m_verts[0].m_color = rootView().themeData().textfield1Inactive();
|
||||
m_verts[1].m_color = rootView().themeData().textfield2Inactive();
|
||||
m_verts[2].m_color = rootView().themeData().textfield1Inactive();
|
||||
m_verts[3].m_color = rootView().themeData().textfield2Inactive();
|
||||
m_verts[4].m_color = rootView().themeData().textfield2Inactive();
|
||||
const ThemeData& theme = rootView().themeData();
|
||||
if (m_error)
|
||||
{
|
||||
m_verts[0].m_color = theme.textfield1Inactive() * Zeus::CColor::skRed;
|
||||
m_verts[1].m_color = theme.textfield2Inactive() * Zeus::CColor::skRed;
|
||||
m_verts[2].m_color = theme.textfield1Inactive() * Zeus::CColor::skRed;
|
||||
m_verts[3].m_color = theme.textfield2Inactive() * Zeus::CColor::skRed;
|
||||
m_verts[4].m_color = theme.textfield2Inactive() * Zeus::CColor::skRed;
|
||||
for (int i=5 ; i<28 ; ++i)
|
||||
m_verts[i].m_color = theme.textfield2Inactive() * Zeus::CColor::skRed;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_verts[0].m_color = theme.textfield1Inactive();
|
||||
m_verts[1].m_color = theme.textfield2Inactive();
|
||||
m_verts[2].m_color = theme.textfield1Inactive();
|
||||
m_verts[3].m_color = theme.textfield2Inactive();
|
||||
m_verts[4].m_color = theme.textfield2Inactive();
|
||||
for (int i=5 ; i<28 ; ++i)
|
||||
m_verts[i].m_color = theme.textfield2Inactive();
|
||||
}
|
||||
m_bVertsBuf->load(m_verts, sizeof(m_verts));
|
||||
m_bgState = BGState::Inactive;
|
||||
}
|
||||
|
||||
void TextField::setHover()
|
||||
{
|
||||
m_verts[0].m_color = rootView().themeData().textfield1Hover();
|
||||
m_verts[1].m_color = rootView().themeData().textfield2Hover();
|
||||
m_verts[2].m_color = rootView().themeData().textfield1Hover();
|
||||
m_verts[3].m_color = rootView().themeData().textfield2Hover();
|
||||
m_verts[4].m_color = rootView().themeData().textfield2Hover();
|
||||
const ThemeData& theme = rootView().themeData();
|
||||
if (m_error)
|
||||
{
|
||||
m_verts[0].m_color = theme.textfield1Hover() * Zeus::CColor::skRed;
|
||||
m_verts[1].m_color = theme.textfield2Hover() * Zeus::CColor::skRed;
|
||||
m_verts[2].m_color = theme.textfield1Hover() * Zeus::CColor::skRed;
|
||||
m_verts[3].m_color = theme.textfield2Hover() * Zeus::CColor::skRed;
|
||||
m_verts[4].m_color = theme.textfield2Hover() * Zeus::CColor::skRed;
|
||||
for (int i=5 ; i<28 ; ++i)
|
||||
m_verts[i].m_color = theme.textfield2Inactive() * Zeus::CColor::skRed;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_verts[0].m_color = theme.textfield1Hover();
|
||||
m_verts[1].m_color = theme.textfield2Hover();
|
||||
m_verts[2].m_color = theme.textfield1Hover();
|
||||
m_verts[3].m_color = theme.textfield2Hover();
|
||||
m_verts[4].m_color = theme.textfield2Hover();
|
||||
for (int i=5 ; i<28 ; ++i)
|
||||
m_verts[i].m_color = theme.textfield2Inactive();
|
||||
}
|
||||
m_bVertsBuf->load(m_verts, sizeof(m_verts));
|
||||
m_bgState = BGState::Hover;
|
||||
}
|
||||
|
||||
void TextField::setDisabled()
|
||||
{
|
||||
m_verts[0].m_color = rootView().themeData().textfield1Disabled();
|
||||
m_verts[1].m_color = rootView().themeData().textfield2Disabled();
|
||||
m_verts[2].m_color = rootView().themeData().textfield1Disabled();
|
||||
m_verts[3].m_color = rootView().themeData().textfield2Disabled();
|
||||
m_verts[4].m_color = rootView().themeData().textfield2Disabled();
|
||||
const ThemeData& theme = rootView().themeData();
|
||||
if (m_error)
|
||||
{
|
||||
m_verts[0].m_color = theme.textfield1Disabled() * Zeus::CColor::skRed;
|
||||
m_verts[1].m_color = theme.textfield2Disabled() * Zeus::CColor::skRed;
|
||||
m_verts[2].m_color = theme.textfield1Disabled() * Zeus::CColor::skRed;
|
||||
m_verts[3].m_color = theme.textfield2Disabled() * Zeus::CColor::skRed;
|
||||
m_verts[4].m_color = theme.textfield2Disabled() * Zeus::CColor::skRed;
|
||||
for (int i=5 ; i<28 ; ++i)
|
||||
m_verts[i].m_color = theme.textfield2Disabled() * Zeus::CColor::skRed;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_verts[0].m_color = theme.textfield1Disabled();
|
||||
m_verts[1].m_color = theme.textfield2Disabled();
|
||||
m_verts[2].m_color = theme.textfield1Disabled();
|
||||
m_verts[3].m_color = theme.textfield2Disabled();
|
||||
m_verts[4].m_color = theme.textfield2Disabled();
|
||||
for (int i=5 ; i<28 ; ++i)
|
||||
m_verts[i].m_color = theme.textfield2Disabled();
|
||||
}
|
||||
m_bVertsBuf->load(m_verts, sizeof(m_verts));
|
||||
m_bgState = BGState::Disabled;
|
||||
}
|
||||
|
||||
void TextField::refreshBg()
|
||||
{
|
||||
switch (m_bgState)
|
||||
{
|
||||
case BGState::Inactive:
|
||||
setInactive(); break;
|
||||
case BGState::Hover:
|
||||
setHover(); break;
|
||||
case BGState::Disabled:
|
||||
setDisabled(); break;
|
||||
}
|
||||
}
|
||||
|
||||
void TextField::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||
|
@ -524,7 +588,6 @@ boo::SWindowRect TextField::rectForCharacterRange(const std::pair<int,int>& rang
|
|||
const std::vector<TextView::RenderGlyph>& glyphs = m_text->accessGlyphs();
|
||||
const TextView::RenderGlyph& g1 = glyphs[range.first];
|
||||
const TextView::RenderGlyph& g2 = glyphs[endIdx];
|
||||
fprintf(stderr, "Ret %d %d\n", subRect().location[0] + int(g1.m_pos[1][0]), subRect().location[1] + int(g1.m_pos[1][1]));
|
||||
return {{subRect().location[0] + int(g1.m_pos[1][0]), subRect().location[1] + int(g1.m_pos[1][1])},
|
||||
{int(g2.m_pos[3][0]-g1.m_pos[1][0]), int(g2.m_pos[0][1]-g1.m_pos[1][1])}};
|
||||
}
|
||||
|
@ -534,6 +597,36 @@ void TextField::think()
|
|||
++m_cursorFrames;
|
||||
++m_clickFrames;
|
||||
++m_clickFrames2;
|
||||
++m_errorFrames;
|
||||
|
||||
if (m_error && m_errorFrames <= 360)
|
||||
{
|
||||
Zeus::CColor errMult;
|
||||
Zeus::CColor errBg;
|
||||
if (m_errorFrames < 300)
|
||||
{
|
||||
errMult = m_viewVertBlock.m_color;
|
||||
errBg = rootView().themeData().tooltipBackground() * m_viewVertBlock.m_color;
|
||||
}
|
||||
else if (m_errorFrames >= 360)
|
||||
{
|
||||
errMult = Zeus::CColor::skClear;
|
||||
errBg = rootView().themeData().tooltipBackground();
|
||||
errBg[3] = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float t = (m_errorFrames - 300) / 60.0;
|
||||
errMult = Zeus::CColor::lerp(m_viewVertBlock.m_color, Zeus::CColor::skClear, t);
|
||||
errBg = Zeus::CColor::lerp(rootView().themeData().tooltipBackground() * m_viewVertBlock.m_color,
|
||||
Zeus::CColor::skClear, t);
|
||||
}
|
||||
for (size_t i=32 ; i<41 ; ++i)
|
||||
m_verts[i].m_color = errBg;
|
||||
m_bVertsBuf->load(m_verts, sizeof(m_verts));
|
||||
|
||||
m_errText->setMultiplyColor(errMult);
|
||||
}
|
||||
|
||||
std::unique_lock<std::recursive_mutex> lk(m_textInputLk);
|
||||
_setText();
|
||||
|
@ -599,6 +692,34 @@ void TextField::setCursorPos(size_t pos)
|
|||
m_deferredCursorPos = pos;
|
||||
m_hasCursorSet = true;
|
||||
}
|
||||
|
||||
void TextField::setErrorState(const std::string& message)
|
||||
{
|
||||
m_error = true;
|
||||
if (m_selectionCount)
|
||||
_reallySetSelectionRange(m_selectionStart, m_selectionCount);
|
||||
else
|
||||
clearSelectionRange();
|
||||
refreshBg();
|
||||
|
||||
m_errText.reset(new TextView(rootView().viewRes(), *this, rootView().viewRes().m_mainFont));
|
||||
m_errText->typesetGlyphs(message, rootView().themeData().uiText());
|
||||
|
||||
updateSize();
|
||||
m_errorFrames = 0;
|
||||
}
|
||||
|
||||
void TextField::clearErrorState()
|
||||
{
|
||||
m_error = false;
|
||||
if (m_selectionCount)
|
||||
_reallySetSelectionRange(m_selectionStart, m_selectionCount);
|
||||
else
|
||||
clearSelectionRange();
|
||||
refreshBg();
|
||||
m_errText.reset();
|
||||
m_errorFrames = 360;
|
||||
}
|
||||
|
||||
void TextField::_reallySetSelectionRange(size_t start, size_t len)
|
||||
{
|
||||
|
@ -609,12 +730,16 @@ void TextField::_reallySetSelectionRange(size_t start, size_t len)
|
|||
std::vector<TextView::RenderGlyph>& glyphs = m_text->accessGlyphs();
|
||||
offset1 += glyphs[start].m_pos[0][0];
|
||||
offset2 += glyphs[start+len-1].m_pos[2][0];
|
||||
const Zeus::CColor& selColor = rootView().themeData().selectedFieldText();
|
||||
const Zeus::CColor& deselColor = m_error ? rootView().themeData().uiText() :
|
||||
rootView().themeData().fieldText();
|
||||
|
||||
for (size_t i=0 ; i<glyphs.size() ; ++i)
|
||||
{
|
||||
if (i >= start && i < start + len)
|
||||
glyphs[i].m_color = rootView().themeData().selectedFieldText();
|
||||
glyphs[i].m_color = selColor;
|
||||
else
|
||||
glyphs[i].m_color = rootView().themeData().fieldText();
|
||||
glyphs[i].m_color = deselColor;
|
||||
}
|
||||
m_text->updateGlyphs();
|
||||
|
||||
|
@ -659,7 +784,7 @@ void TextField::_setSelectionRange()
|
|||
{
|
||||
size_t len = UTF8Iterator(m_textStr.cbegin()).countTo(m_textStr.cend());
|
||||
m_selectionStart = std::min(m_deferredSelectionStart, len-1);
|
||||
m_deferredSelectionStart = m_selectionStart;
|
||||
m_deferredSelectionStart = m_selectionStart;
|
||||
m_selectionCount = std::min(m_deferredSelectionCount, len-m_selectionStart);
|
||||
m_deferredSelectionCount = m_selectionCount;
|
||||
_reallySetSelectionRange(m_selectionStart, m_selectionCount);
|
||||
|
@ -690,9 +815,12 @@ void TextField::_clearSelectionRange()
|
|||
m_selectionStart = 0;
|
||||
m_selectionCount = 0;
|
||||
|
||||
const Zeus::CColor& deselColor = m_error ? rootView().themeData().uiText() :
|
||||
rootView().themeData().fieldText();
|
||||
|
||||
std::vector<TextView::RenderGlyph>& glyphs = m_text->accessGlyphs();
|
||||
for (size_t i=0 ; i<glyphs.size() ; ++i)
|
||||
glyphs[i].m_color = rootView().themeData().fieldText();
|
||||
glyphs[i].m_color = deselColor;
|
||||
m_text->updateGlyphs();
|
||||
|
||||
m_hasSelectionClear = false;
|
||||
|
@ -750,6 +878,30 @@ void TextField::resized(const boo::SWindowRect& root, const boo::SWindowRect& su
|
|||
m_verts[26].m_pos.assign(width+1, 1, 0);
|
||||
m_verts[27].m_pos.assign(width+1, 0, 0);
|
||||
|
||||
if (m_error)
|
||||
{
|
||||
boo::SWindowRect errRect = sub;
|
||||
errRect.location[1] -= 16 * pf;
|
||||
errRect.location[0] += 5 * pf;
|
||||
m_errText->resized(root, errRect);
|
||||
|
||||
int eX = 0;
|
||||
int eY = -22 * pf;
|
||||
int eWidth = m_errText->nominalWidth() + 10 * pf;
|
||||
int eHeight = 20 * pf;
|
||||
m_verts[32].m_pos.assign(eX, eY + eHeight, 0);
|
||||
m_verts[33].m_pos.assign(eX, eY, 0);
|
||||
m_verts[34].m_pos.assign(eX + eWidth, eY + eHeight, 0);
|
||||
m_verts[35].m_pos.assign(eX + eWidth, eY, 0);
|
||||
m_verts[36] = m_verts[35];
|
||||
m_verts[37].m_pos.assign(eX + 7 * pf, eY + eHeight + 7 * pf, 0);
|
||||
m_verts[38] = m_verts[37];
|
||||
m_verts[39].m_pos.assign(eX, eY + eHeight, 0);
|
||||
m_verts[40].m_pos.assign(eX + 14 * pf, eY + eHeight, 0);
|
||||
for (size_t i=32 ; i<41 ; ++i)
|
||||
m_verts[i].m_color = Zeus::CColor::skClear;
|
||||
}
|
||||
|
||||
m_bVertsBuf->load(m_verts, sizeof(m_verts));
|
||||
|
||||
m_nomWidth = width;
|
||||
|
@ -777,6 +929,13 @@ void TextField::draw(boo::IGraphicsCommandQueue* gfxQ)
|
|||
else
|
||||
gfxQ->draw(28, 4);
|
||||
}
|
||||
|
||||
if (m_error)
|
||||
{
|
||||
gfxQ->draw(32, 9);
|
||||
m_errText->draw(gfxQ);
|
||||
}
|
||||
|
||||
m_text->draw(gfxQ);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue