mirror of https://github.com/AxioDL/metaforce.git
boo lambda-API refactor
This commit is contained in:
parent
83b89ceee6
commit
b756005aad
|
@ -81,7 +81,7 @@ public:
|
|||
friend class ViewResources;
|
||||
friend class Button;
|
||||
|
||||
void init(boo::IGraphicsDataFactory* factory, const IThemeData& theme);
|
||||
void init(boo::IGraphicsDataFactory::Context& ctx, const IThemeData& theme);
|
||||
};
|
||||
|
||||
~Button() {closeMenu({});}
|
||||
|
@ -116,7 +116,8 @@ public:
|
|||
{
|
||||
View::setMultiplyColor(color);
|
||||
m_viewVertBlock.m_color = color;
|
||||
m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(ViewBlock));
|
||||
if (m_viewVertBlockBuf)
|
||||
m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(ViewBlock));
|
||||
m_text->setMultiplyColor(color);
|
||||
if (m_icon)
|
||||
m_icon->setMultiplyColor(color);
|
||||
|
|
|
@ -40,7 +40,7 @@ class PathButtons : public ScrollView
|
|||
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||
|
||||
ContentView(ViewResources& res, PathButtons& pb)
|
||||
: View(res, pb), m_pb(pb) {commitResources(res);}
|
||||
: View(res, pb), m_pb(pb) {}
|
||||
};
|
||||
ViewChild<std::unique_ptr<ContentView>> m_contentView;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "IMenuNode.hpp"
|
||||
#include "DeferredWindowEvents.hpp"
|
||||
#include "IViewManager.hpp"
|
||||
#include "optional.hpp"
|
||||
#include <boo/boo.hpp>
|
||||
|
||||
namespace specter
|
||||
|
@ -69,7 +70,7 @@ class RootView : public View
|
|||
void resized();
|
||||
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||
|
||||
SplitMenuSystem(RootView& rv);
|
||||
SplitMenuSystem(RootView& rv, boo::IGraphicsDataFactory::Context& ctx);
|
||||
const std::string* text() const {return &m_text;}
|
||||
size_t subNodeCount() const {return 2;}
|
||||
IMenuNode* subNode(size_t idx)
|
||||
|
@ -108,7 +109,8 @@ class RootView : public View
|
|||
m_smn.m_deferredCoord = coord;
|
||||
}
|
||||
} m_joinActionNode;
|
||||
} m_splitMenuSystem;
|
||||
};
|
||||
std::experimental::optional<SplitMenuSystem> m_splitMenuSystem;
|
||||
|
||||
public:
|
||||
RootView(IViewManager& viewMan, ViewResources& res, boo::IWindow* window);
|
||||
|
@ -223,9 +225,9 @@ public:
|
|||
|
||||
void beginInteractiveJoin(SplitView* sv, const boo::SWindowCoord& coord)
|
||||
{
|
||||
m_splitMenuSystem.m_phase = SplitMenuSystem::Phase::InteractiveJoin;
|
||||
m_splitMenuSystem.m_splitView = sv;
|
||||
m_splitMenuSystem.mouseMove(coord);
|
||||
m_splitMenuSystem->m_phase = SplitMenuSystem::Phase::InteractiveJoin;
|
||||
m_splitMenuSystem->m_splitView = sv;
|
||||
m_splitMenuSystem->mouseMove(coord);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
friend class SplitView;
|
||||
boo::ITextureS* m_shadingTex;
|
||||
|
||||
void init(boo::IGraphicsDataFactory* factory, const IThemeData& theme);
|
||||
void init(boo::IGraphicsDataFactory::Context& ctx, const IThemeData& theme);
|
||||
};
|
||||
|
||||
enum class ArrowDir
|
||||
|
|
|
@ -117,7 +117,8 @@ public:
|
|||
{
|
||||
View::setMultiplyColor(color);
|
||||
m_viewVertBlock.m_color = color;
|
||||
m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(ViewBlock));
|
||||
if (m_viewVertBlockBuf)
|
||||
m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(ViewBlock));
|
||||
m_text->setMultiplyColor(color);
|
||||
if (m_errText)
|
||||
m_errText->setMultiplyColor(color);
|
||||
|
|
|
@ -47,15 +47,15 @@ public:
|
|||
boo::IShaderPipeline* m_subpixel = nullptr;
|
||||
boo::IVertexFormat* m_vtxFmt = nullptr; /* Not OpenGL */
|
||||
|
||||
void init(boo::GLDataFactory* factory, FontCache* fcache);
|
||||
void init(boo::GLDataFactory::Context& ctx, FontCache* fcache);
|
||||
#if _WIN32
|
||||
void init(boo::ID3DDataFactory* factory, FontCache* fcache);
|
||||
void init(boo::ID3DDataFactory::Context& ctx, FontCache* fcache);
|
||||
#endif
|
||||
#if BOO_HAS_METAL
|
||||
void init(boo::MetalDataFactory* factory, FontCache* fcache);
|
||||
void init(boo::MetalDataFactory::Context& ctx, FontCache* fcache);
|
||||
#endif
|
||||
#if BOO_HAS_VULKAN
|
||||
void init(boo::VulkanDataFactory* factory, FontCache* fcache);
|
||||
void init(boo::VulkanDataFactory::Context& ctx, FontCache* fcache);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ public:
|
|||
friend class Toolbar;
|
||||
boo::ITextureS* m_shadingTex;
|
||||
|
||||
void init(boo::IGraphicsDataFactory* factory, const IThemeData& theme);
|
||||
void init(boo::IGraphicsDataFactory::Context& ctx, const IThemeData& theme);
|
||||
};
|
||||
|
||||
enum class Position
|
||||
|
|
|
@ -110,10 +110,15 @@ public:
|
|||
boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */
|
||||
boo::IShaderDataBinding* m_shaderBinding = nullptr;
|
||||
|
||||
void initSolid(ViewResources& res, size_t count, boo::IGraphicsBuffer* viewBlockBuf);
|
||||
void initTex(ViewResources& res, size_t count, boo::IGraphicsBuffer* viewBlockBuf, boo::ITexture* texture);
|
||||
void initSolid(boo::IGraphicsDataFactory::Context& ctx,
|
||||
ViewResources& res, size_t count,
|
||||
boo::IGraphicsBuffer* viewBlockBuf);
|
||||
void initTex(boo::IGraphicsDataFactory::Context& ctx,
|
||||
ViewResources& res, size_t count,
|
||||
boo::IGraphicsBuffer* viewBlockBuf,
|
||||
boo::ITexture* texture);
|
||||
|
||||
void load(const void* data, size_t sz) {m_vertsBuf->load(data, sz);}
|
||||
void load(const void* data, size_t sz) {if (m_vertsBuf) m_vertsBuf->load(data, sz);}
|
||||
operator boo::IShaderDataBinding*() {return m_shaderBinding;}
|
||||
};
|
||||
private:
|
||||
|
@ -125,7 +130,6 @@ private:
|
|||
boo::GraphicsDataToken m_gfxData;
|
||||
|
||||
friend class RootView;
|
||||
void buildResources(ViewResources& res);
|
||||
View(ViewResources& res);
|
||||
|
||||
protected:
|
||||
|
@ -148,7 +152,7 @@ protected:
|
|||
" float4x4 mv;\n"\
|
||||
" float4 mulColor;\n"\
|
||||
"};\n"
|
||||
boo::IGraphicsBufferD* m_viewVertBlockBuf;
|
||||
boo::IGraphicsBufferD* m_viewVertBlockBuf = nullptr;
|
||||
|
||||
public:
|
||||
struct Resources
|
||||
|
@ -159,21 +163,22 @@ public:
|
|||
boo::IShaderPipeline* m_texShader = nullptr;
|
||||
boo::IVertexFormat* m_texVtxFmt = nullptr; /* Not OpenGL */
|
||||
|
||||
void init(boo::GLDataFactory* factory, const IThemeData& theme);
|
||||
void init(boo::GLDataFactory::Context& ctx, const IThemeData& theme);
|
||||
#if _WIN32
|
||||
void init(boo::ID3DDataFactory* factory, const IThemeData& theme);
|
||||
void init(boo::ID3DDataFactory::Context& ctx, const IThemeData& theme);
|
||||
#endif
|
||||
#if BOO_HAS_METAL
|
||||
void init(boo::MetalDataFactory* factory, const IThemeData& theme);
|
||||
void init(boo::MetalDataFactory::Context& ctx, const IThemeData& theme);
|
||||
#endif
|
||||
#if BOO_HAS_VULKAN
|
||||
void init(boo::VulkanDataFactory* factory, const IThemeData& theme);
|
||||
void init(boo::VulkanDataFactory::Context& ctx, const IThemeData& theme);
|
||||
#endif
|
||||
};
|
||||
|
||||
protected:
|
||||
View(ViewResources& res, View& parentView);
|
||||
void commitResources(ViewResources& res);
|
||||
void buildResources(boo::IGraphicsDataFactory::Context& ctx, ViewResources& res);
|
||||
void commitResources(ViewResources& res, const boo::FactoryCommitFunc& commitFunc);
|
||||
|
||||
public:
|
||||
virtual ~View() {}
|
||||
|
@ -199,7 +204,8 @@ public:
|
|||
virtual void setMultiplyColor(const zeus::CColor& color)
|
||||
{
|
||||
m_viewVertBlock.m_color = color;
|
||||
m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(ViewBlock));
|
||||
if (m_viewVertBlockBuf)
|
||||
m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(ViewBlock));
|
||||
}
|
||||
|
||||
virtual int nominalWidth() const {return 0;}
|
||||
|
|
|
@ -149,8 +149,8 @@ public:
|
|||
|
||||
class ViewResources
|
||||
{
|
||||
template <class Factory>
|
||||
void init(Factory* factory, const IThemeData& theme, FontCache* fcache)
|
||||
template <class FactoryCtx>
|
||||
void init(FactoryCtx& factory, const IThemeData& theme, FontCache* fcache)
|
||||
{
|
||||
m_viewRes.init(factory, theme);
|
||||
m_textRes.init(factory, fcache);
|
||||
|
@ -160,7 +160,7 @@ class ViewResources
|
|||
}
|
||||
|
||||
public:
|
||||
boo::IGraphicsDataFactory* m_factory = nullptr;
|
||||
boo::IGraphicsDataFactory* m_factory;
|
||||
FontCache* m_fcache = nullptr;
|
||||
View::Resources m_viewRes;
|
||||
TextView::Resources m_textRes;
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace specter
|
|||
{
|
||||
static logvisor::Module Log("specter::Button");
|
||||
|
||||
void Button::Resources::init(boo::IGraphicsDataFactory* factory, const IThemeData& theme)
|
||||
void Button::Resources::init(boo::IGraphicsDataFactory::Context& ctx, const IThemeData& theme)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,12 @@ Button::Button(ViewResources& res, View& parentView,
|
|||
: Control(res, parentView, controlBinding),
|
||||
m_style(style), m_textColor(textColor), m_bgColor(bgColor), m_textStr(text), m_constraint(constraint)
|
||||
{
|
||||
m_vertsBinding.initSolid(res, 40, m_viewVertBlockBuf);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_vertsBinding.initSolid(ctx, res, 40, m_viewVertBlockBuf);
|
||||
return true;
|
||||
});
|
||||
|
||||
m_buttonTarget.m_view.reset(new ButtonTarget(res, *this));
|
||||
m_menuTarget.m_view.reset(new MenuTarget(res, *this));
|
||||
|
|
|
@ -60,7 +60,6 @@ FileBrowser::FileBrowser(ViewResources& res, View& parentView, const std::string
|
|||
m_recentBookmarkBind(*this),
|
||||
m_returnFunc(returnFunc)
|
||||
{
|
||||
commitResources(res);
|
||||
setBackground({0,0,0,0.5});
|
||||
|
||||
IViewManager& vm = rootView().viewManager();
|
||||
|
|
|
@ -390,10 +390,14 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
|
|||
}
|
||||
|
||||
WriteCompressed(writer, (atUint8*)texmap.get(), bufSz);
|
||||
m_tex =
|
||||
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||
boo::TextureFormat::RGBA8, texmap.get(), bufSz);
|
||||
m_token = gf->commit();
|
||||
|
||||
m_token = gf->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
m_tex =
|
||||
ctx.newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||
boo::TextureFormat::RGBA8, texmap.get(), bufSz);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -471,10 +475,14 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
|
|||
}
|
||||
|
||||
WriteCompressed(writer, (atUint8*)texmap.get(), bufSz);
|
||||
m_tex =
|
||||
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||
boo::TextureFormat::I8, texmap.get(), bufSz);
|
||||
m_token = gf->commit();
|
||||
|
||||
m_token = gf->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
m_tex =
|
||||
ctx.newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||
boo::TextureFormat::I8, texmap.get(), bufSz);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
buildKernTable(face);
|
||||
|
@ -591,10 +599,14 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
|
|||
|
||||
if (!ReadDecompressed(reader, (atUint8*)texmap.get(), bufSz))
|
||||
return;
|
||||
m_tex =
|
||||
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||
boo::TextureFormat::RGBA8, texmap.get(), bufSz);
|
||||
m_token = gf->commit();
|
||||
|
||||
m_token = gf->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
m_tex =
|
||||
ctx.newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||
boo::TextureFormat::RGBA8, texmap.get(), bufSz);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -672,10 +684,14 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi,
|
|||
|
||||
if (!ReadDecompressed(reader, (atUint8*)texmap.get(), bufSz))
|
||||
return;
|
||||
m_tex =
|
||||
gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||
boo::TextureFormat::I8, texmap.get(), bufSz);
|
||||
m_token = gf->commit();
|
||||
|
||||
m_token = gf->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
m_tex =
|
||||
ctx.newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1,
|
||||
boo::TextureFormat::I8, texmap.get(), bufSz);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
buildKernTable(face);
|
||||
|
|
|
@ -7,8 +7,12 @@ namespace specter
|
|||
IconView::IconView(ViewResources& res, View& parentView, Icon& icon)
|
||||
: View(res, parentView)
|
||||
{
|
||||
m_vertexBinding.initTex(res, 4, m_viewVertBlockBuf, icon.m_tex);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_vertexBinding.initTex(ctx, res, 4, m_viewVertBlockBuf, icon.m_tex);
|
||||
return true;
|
||||
});
|
||||
TexShaderVert verts[] =
|
||||
{
|
||||
{{0, 1, 0}, icon.m_uvCoords[0]},
|
||||
|
|
|
@ -11,8 +11,12 @@ namespace specter
|
|||
Menu::Menu(ViewResources& res, View& parentView, IMenuNode* rootNode)
|
||||
: View(res, parentView)
|
||||
{
|
||||
m_vertsBinding.initSolid(res, 8, m_viewVertBlockBuf);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_vertsBinding.initSolid(ctx, res, 8, m_viewVertBlockBuf);
|
||||
return true;
|
||||
});
|
||||
m_headText.reset(new TextView(res, *this, res.m_mainFont));
|
||||
m_scroll.m_view.reset(new ScrollView(res, *this, ScrollView::Style::ThinIndicator));
|
||||
m_content.reset(new ContentView(res, *this));
|
||||
|
@ -68,8 +72,12 @@ void Menu::reset(IMenuNode* rootNode)
|
|||
Menu::Menu(ViewResources& res, View& parentView, IMenuNode* rootNode, IMenuNode* thisNode)
|
||||
: View(res, parentView), m_rootNode(rootNode), m_thisNode(thisNode)
|
||||
{
|
||||
m_vertsBinding.initSolid(res, 8, m_viewVertBlockBuf);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_vertsBinding.initSolid(ctx, res, 8, m_viewVertBlockBuf);
|
||||
return true;
|
||||
});
|
||||
m_headText.reset(new TextView(res, *this, res.m_mainFont));
|
||||
m_scroll.m_view.reset(new ScrollView(res, *this, ScrollView::Style::ThinIndicator));
|
||||
m_content.reset(new ContentView(res, *this));
|
||||
|
@ -79,8 +87,12 @@ Menu::Menu(ViewResources& res, View& parentView, IMenuNode* rootNode, IMenuNode*
|
|||
Menu::ContentView::ContentView(ViewResources& res, Menu& menu)
|
||||
: View(res, menu), m_menu(menu)
|
||||
{
|
||||
m_hlVertsBinding.initSolid(res, 4, m_viewVertBlockBuf);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_hlVertsBinding.initSolid(ctx, res, 4, m_viewVertBlockBuf);
|
||||
return true;
|
||||
});
|
||||
|
||||
m_hlVerts[0].m_color = res.themeData().button1Hover();
|
||||
m_hlVerts[1].m_color = res.themeData().button2Hover();
|
||||
|
@ -91,7 +103,11 @@ Menu::ContentView::ContentView(ViewResources& res, Menu& menu)
|
|||
Menu::ItemView::ItemView(ViewResources& res, Menu& menu, const std::string& text, size_t idx, IMenuNode* node)
|
||||
: View(res, menu), m_menu(menu), m_idx(idx), m_node(node)
|
||||
{
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
return true;
|
||||
});
|
||||
m_textView.reset(new specter::TextView(res, *this, res.m_mainFont));
|
||||
m_textView->typesetGlyphs(text, res.themeData().uiText());
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@ MessageWindow::MessageWindow(ViewResources& res, View& parentView,
|
|||
m_okBind(*this, rootView().viewManager().translateOr("ok", "OK")),
|
||||
m_cancelBind(*this, rootView().viewManager().translateOr("cancel", "Cancel"))
|
||||
{
|
||||
commitResources(res);
|
||||
|
||||
m_text.reset(new MultiLineTextView(res, *this, res.m_mainFont, TextView::Alignment::Center));
|
||||
m_text->typesetGlyphs(message, res.themeData().uiText(), 380 * res.pixelFactor());
|
||||
constraint() = RectangleConstraint(400 * res.pixelFactor(), 80 * res.pixelFactor() + m_text->nominalHeight());
|
||||
|
|
|
@ -278,7 +278,9 @@ void ModalWindow::setFillColors(float t)
|
|||
ModalWindow::ModalWindow(ViewResources& res, View& parentView, const RectangleConstraint& constraint)
|
||||
: ModalWindow(res, parentView, constraint, res.themeData().splashBackground()) {}
|
||||
|
||||
ModalWindow::ModalWindow(ViewResources& res, View& parentView, const RectangleConstraint& constraint, const zeus::CColor& bgColor)
|
||||
ModalWindow::ModalWindow(ViewResources& res, View& parentView,
|
||||
const RectangleConstraint& constraint,
|
||||
const zeus::CColor& bgColor)
|
||||
: View(res, parentView),
|
||||
m_constraint(constraint),
|
||||
m_windowBg(bgColor),
|
||||
|
@ -289,11 +291,14 @@ ModalWindow::ModalWindow(ViewResources& res, View& parentView, const RectangleCo
|
|||
{
|
||||
m_windowBgClear[3] = 0.0;
|
||||
m_line2Clear[3] = 0.0;
|
||||
m_viewBlockBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1);
|
||||
|
||||
m_vertsBinding.initSolid(res, 38, m_viewBlockBuf);
|
||||
|
||||
m_windowGfxData = res.m_factory->commit();
|
||||
|
||||
m_windowGfxData = res.m_factory->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_viewBlockBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1);
|
||||
m_vertsBinding.initSolid(ctx, res, 38, m_viewBlockBuf);
|
||||
return true;
|
||||
});
|
||||
|
||||
for (int i=0 ; i<4 ; ++i)
|
||||
{
|
||||
|
|
|
@ -139,7 +139,11 @@ MultiLineTextView::MultiLineTextView(ViewResources& res,
|
|||
m_lineCapacity(lineCapacity),
|
||||
m_lineHeight(lineHeight)
|
||||
{
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
MultiLineTextView::MultiLineTextView(ViewResources& res,
|
||||
|
|
|
@ -8,22 +8,26 @@ namespace specter
|
|||
static logvisor::Module Log("specter::RootView");
|
||||
|
||||
RootView::RootView(IViewManager& viewMan, ViewResources& res, boo::IWindow* window)
|
||||
: View(res), m_window(window), m_viewMan(viewMan), m_viewRes(&res), m_events(*this),
|
||||
m_splitMenuSystem(*this)
|
||||
: View(res), m_window(window), m_viewMan(viewMan), m_viewRes(&res), m_events(*this)
|
||||
{
|
||||
window->setCallback(&m_events);
|
||||
boo::SWindowRect rect = window->getWindowFrame();
|
||||
m_renderTex = res.m_factory->newRenderTexture(rect.size[0], rect.size[1], true, false);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_splitMenuSystem.emplace(*this, ctx);
|
||||
m_renderTex = ctx.newRenderTexture(rect.size[0], rect.size[1], true, false);
|
||||
return true;
|
||||
});
|
||||
resized(rect, rect);
|
||||
}
|
||||
|
||||
RootView::SplitMenuSystem::SplitMenuSystem(RootView& rv)
|
||||
RootView::SplitMenuSystem::SplitMenuSystem(RootView& rv, boo::IGraphicsDataFactory::Context& ctx)
|
||||
: m_rv(rv), m_text(rv.m_viewMan.translateOr("boundary_action", "Boundary Action")),
|
||||
m_splitActionNode(*this), m_joinActionNode(*this)
|
||||
{
|
||||
m_viewVertBlockBuf = rv.m_viewRes->m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(View::ViewBlock), 1);
|
||||
m_vertsBinding.initSolid(*rv.m_viewRes, 32, m_viewVertBlockBuf);
|
||||
m_viewVertBlockBuf = ctx.newDynamicBuffer(boo::BufferUse::Vertex, sizeof(View::ViewBlock), 1);
|
||||
m_vertsBinding.initSolid(ctx, *rv.m_viewRes, 32, m_viewVertBlockBuf);
|
||||
|
||||
zeus::CColor col = {0.0,0.0,0.0,0.5};
|
||||
for (int i=0 ; i<32 ; ++i)
|
||||
|
@ -153,7 +157,7 @@ void RootView::resized(const boo::SWindowRect& root, const boo::SWindowRect&)
|
|||
m_rightClickMenuRootAndLoc.location[1] *= hr;
|
||||
m_rightClickMenu.m_view->resized(root, m_rightClickMenuRootAndLoc);
|
||||
}
|
||||
m_splitMenuSystem.resized();
|
||||
m_splitMenuSystem->resized();
|
||||
m_resizeRTDirty = true;
|
||||
}
|
||||
|
||||
|
@ -177,9 +181,9 @@ void RootView::SplitMenuSystem::resized()
|
|||
|
||||
void RootView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods)
|
||||
{
|
||||
if (m_splitMenuSystem.m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
if (m_splitMenuSystem->m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
{
|
||||
m_splitMenuSystem.mouseDown(coord, button, mods);
|
||||
m_splitMenuSystem->mouseDown(coord, button, mods);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -207,8 +211,8 @@ void RootView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton butto
|
|||
}
|
||||
else if (button == boo::EMouseButton::Secondary)
|
||||
{
|
||||
m_splitMenuSystem.m_splitView = m_hoverSplitDragView;
|
||||
adoptRightClickMenu(std::make_unique<specter::Menu>(*m_viewRes, *this, &m_splitMenuSystem), coord);
|
||||
m_splitMenuSystem->m_splitView = m_hoverSplitDragView;
|
||||
adoptRightClickMenu(std::make_unique<specter::Menu>(*m_viewRes, *this, &*m_splitMenuSystem), coord);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -247,9 +251,9 @@ void RootView::SplitMenuSystem::mouseDown(const boo::SWindowCoord& coord, boo::E
|
|||
|
||||
void RootView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods)
|
||||
{
|
||||
if (m_splitMenuSystem.m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
if (m_splitMenuSystem->m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
{
|
||||
m_splitMenuSystem.mouseUp(coord, button, mods);
|
||||
m_splitMenuSystem->mouseUp(coord, button, mods);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -340,9 +344,9 @@ SplitView* RootView::recursiveTestSplitHover(SplitView* sv, const boo::SWindowCo
|
|||
|
||||
void RootView::mouseMove(const boo::SWindowCoord& coord)
|
||||
{
|
||||
if (m_splitMenuSystem.m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
if (m_splitMenuSystem->m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
{
|
||||
m_splitMenuSystem.mouseMove(coord);
|
||||
m_splitMenuSystem->mouseMove(coord);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -455,9 +459,9 @@ void RootView::mouseEnter(const boo::SWindowCoord& coord)
|
|||
|
||||
void RootView::mouseLeave(const boo::SWindowCoord& coord)
|
||||
{
|
||||
if (m_splitMenuSystem.m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
if (m_splitMenuSystem->m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
{
|
||||
m_splitMenuSystem.mouseLeave(coord);
|
||||
m_splitMenuSystem->mouseLeave(coord);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -543,9 +547,9 @@ void RootView::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool
|
|||
m_window->setFullscreen(!m_window->isFullscreen());
|
||||
return;
|
||||
}
|
||||
if (key == boo::ESpecialKey::Esc && m_splitMenuSystem.m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
if (key == boo::ESpecialKey::Esc && m_splitMenuSystem->m_phase != SplitMenuSystem::Phase::Inactive)
|
||||
{
|
||||
m_splitMenuSystem.m_phase = SplitMenuSystem::Phase::Inactive;
|
||||
m_splitMenuSystem->m_phase = SplitMenuSystem::Phase::Inactive;
|
||||
return;
|
||||
}
|
||||
for (View* v : m_views)
|
||||
|
@ -589,20 +593,20 @@ void RootView::displayTooltip(const std::string& name, const std::string& help)
|
|||
|
||||
void RootView::internalThink()
|
||||
{
|
||||
if (m_splitMenuSystem.m_deferredSplit)
|
||||
if (m_splitMenuSystem->m_deferredSplit)
|
||||
{
|
||||
m_splitMenuSystem.m_deferredSplit = false;
|
||||
m_splitMenuSystem->m_deferredSplit = false;
|
||||
m_rightClickMenu.m_view.reset();
|
||||
m_splitMenuSystem.m_phase = SplitMenuSystem::Phase::InteractiveSplit;
|
||||
m_splitMenuSystem.mouseMove(m_splitMenuSystem.m_deferredCoord);
|
||||
m_splitMenuSystem->m_phase = SplitMenuSystem::Phase::InteractiveSplit;
|
||||
m_splitMenuSystem->mouseMove(m_splitMenuSystem->m_deferredCoord);
|
||||
}
|
||||
|
||||
if (m_splitMenuSystem.m_deferredJoin)
|
||||
if (m_splitMenuSystem->m_deferredJoin)
|
||||
{
|
||||
m_splitMenuSystem.m_deferredJoin = false;
|
||||
m_splitMenuSystem->m_deferredJoin = false;
|
||||
m_rightClickMenu.m_view.reset();
|
||||
m_splitMenuSystem.m_phase = SplitMenuSystem::Phase::InteractiveJoin;
|
||||
m_splitMenuSystem.mouseMove(m_splitMenuSystem.m_deferredCoord);
|
||||
m_splitMenuSystem->m_phase = SplitMenuSystem::Phase::InteractiveJoin;
|
||||
m_splitMenuSystem->mouseMove(m_splitMenuSystem->m_deferredCoord);
|
||||
}
|
||||
|
||||
if (m_rightClickMenu.m_view)
|
||||
|
@ -625,7 +629,7 @@ void RootView::draw(boo::IGraphicsCommandQueue* gfxQ)
|
|||
v->draw(gfxQ);
|
||||
if (m_tooltip)
|
||||
m_tooltip->draw(gfxQ);
|
||||
m_splitMenuSystem.draw(gfxQ);
|
||||
m_splitMenuSystem->draw(gfxQ);
|
||||
if (m_rightClickMenu.m_view)
|
||||
m_rightClickMenu.m_view->draw(gfxQ);
|
||||
gfxQ->resolveDisplay(m_renderTex);
|
||||
|
|
|
@ -10,8 +10,12 @@ namespace specter
|
|||
ScrollView::ScrollView(ViewResources& res, View& parentView, Style style)
|
||||
: View(res, parentView), m_style(style), m_sideButtonBind(*this, rootView().viewManager())
|
||||
{
|
||||
m_vertsBinding.initSolid(res, 4, m_viewVertBlockBuf);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_vertsBinding.initSolid(ctx, res, 4, m_viewVertBlockBuf);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (style == Style::SideButtons)
|
||||
{
|
||||
|
|
|
@ -20,7 +20,11 @@ Space::Space(ViewResources& res, View& parentView, ISpaceController& controller,
|
|||
Toolbar::Position tbPos, unsigned tbUnits)
|
||||
: View(res, parentView), m_controller(controller), m_tbPos(tbPos)
|
||||
{
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
return true;
|
||||
});
|
||||
setBackground(res.themeData().spaceBackground());
|
||||
static const zeus::CColor triColor = {0.75, 0.75, 0.75, 1.0};
|
||||
if (controller.spaceSplitAllowed())
|
||||
|
@ -32,7 +36,12 @@ Space::Space(ViewResources& res, View& parentView, ISpaceController& controller,
|
|||
Space::CornerView::CornerView(ViewResources& res, Space& space, const zeus::CColor& triColor)
|
||||
: View(res, space), m_space(space)
|
||||
{
|
||||
m_vertexBinding.initSolid(res, 34, m_viewVertBlockBuf);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_vertexBinding.initSolid(ctx, res, 34, m_viewVertBlockBuf);
|
||||
return true;
|
||||
});
|
||||
float pf = res.pixelFactor();
|
||||
|
||||
zeus::CColor edgeColor1 = triColor * res.themeData().spaceTriangleShading1();
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace specter
|
|||
{
|
||||
static logvisor::Module Log("specter::SplitView");
|
||||
|
||||
void SplitView::Resources::init(boo::IGraphicsDataFactory* factory, const IThemeData& theme)
|
||||
void SplitView::Resources::init(boo::IGraphicsDataFactory::Context& ctx, const IThemeData& theme)
|
||||
{
|
||||
static const zeus::RGBA32 tex[3] =
|
||||
{
|
||||
|
@ -16,7 +16,7 @@ void SplitView::Resources::init(boo::IGraphicsDataFactory* factory, const ITheme
|
|||
{0,0,0,255},
|
||||
{255,255,255,64}
|
||||
};
|
||||
m_shadingTex = factory->newStaticTexture(3, 1, 1, boo::TextureFormat::RGBA8, tex, 12);
|
||||
m_shadingTex = ctx.newStaticTexture(3, 1, 1, boo::TextureFormat::RGBA8, tex, 12);
|
||||
}
|
||||
|
||||
SplitView::SplitView(ViewResources& res, View& parentView, ISplitSpaceController* controller,
|
||||
|
@ -24,9 +24,13 @@ SplitView::SplitView(ViewResources& res, View& parentView, ISplitSpaceController
|
|||
: View(res, parentView), m_controller(controller), m_axis(axis), m_slide(split),
|
||||
m_clearanceA(clearanceA), m_clearanceB(clearanceB)
|
||||
{
|
||||
m_splitBlockBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1);
|
||||
m_splitVertsBinding.initTex(res, 4, m_splitBlockBuf, res.m_splitRes.m_shadingTex);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_splitBlockBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1);
|
||||
m_splitVertsBinding.initTex(ctx, res, 4, m_splitBlockBuf, res.m_splitRes.m_shadingTex);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
View* SplitView::setContentView(int slot, View* view)
|
||||
|
|
|
@ -17,19 +17,28 @@ Table::Table(ViewResources& res, View& parentView, ITableDataBinding* data,
|
|||
if (!maxColumns)
|
||||
Log.report(logvisor::Fatal, "0-column tables not supported");
|
||||
|
||||
m_vertsBinding.initSolid(res, maxColumns * 6, m_viewVertBlockBuf);
|
||||
commitResources(res);
|
||||
|
||||
m_scroll.m_view.reset(new ScrollView(res, *this, ScrollView::Style::ThinIndicator));
|
||||
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_vertsBinding.initSolid(ctx, res, maxColumns * 6, m_viewVertBlockBuf);
|
||||
return true;
|
||||
});
|
||||
m_scroll.m_view->setContentView(&m_rowsView);
|
||||
|
||||
updateData();
|
||||
}
|
||||
|
||||
Table::RowsView::RowsView(Table& t, ViewResources& res)
|
||||
: View(res, t), m_t(t), m_verts(new SolidShaderVert[SPECTER_TABLE_MAX_ROWS * t.m_maxColumns * 6])
|
||||
{
|
||||
m_vertsBinding.initSolid(res, SPECTER_TABLE_MAX_ROWS * t.m_maxColumns * 6, m_viewVertBlockBuf);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_vertsBinding.initSolid(ctx, res, SPECTER_TABLE_MAX_ROWS * t.m_maxColumns * 6, m_viewVertBlockBuf);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
Table::CellView::CellView(Table& t, ViewResources& res)
|
||||
|
|
|
@ -8,8 +8,12 @@ namespace specter
|
|||
TextField::TextField(ViewResources& res, View& parentView, IStringBinding* strBind)
|
||||
: ITextInputView(res, parentView, strBind)
|
||||
{
|
||||
m_vertsBinding.initSolid(res, 41, m_viewVertBlockBuf);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_vertsBinding.initSolid(ctx, res, 41, m_viewVertBlockBuf);
|
||||
return true;
|
||||
});
|
||||
|
||||
for (int i=28 ; i<32 ; ++i)
|
||||
m_verts[i].m_color = res.themeData().textfieldSelection();
|
||||
|
|
|
@ -65,26 +65,26 @@ BOO_GLSL_BINDING_HEAD
|
|||
" blendOut = colorOut.a * texture(fontTex, vtf.uv);\n"
|
||||
"}\n";
|
||||
|
||||
void TextView::Resources::init(boo::GLDataFactory* factory, FontCache* fcache)
|
||||
void TextView::Resources::init(boo::GLDataFactory::Context& ctx, FontCache* fcache)
|
||||
{
|
||||
m_fcache = fcache;
|
||||
|
||||
static const char* BlockNames[] = {"SpecterViewBlock"};
|
||||
|
||||
m_regular =
|
||||
factory->newShaderPipeline(GLSLVS, GLSLFSReg, 1, "fontTex", 1, BlockNames,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
ctx.newShaderPipeline(GLSLVS, GLSLFSReg, 1, "fontTex", 1, BlockNames,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
|
||||
m_subpixel =
|
||||
factory->newShaderPipeline(GLSLVS, GLSLFSSubpixel, 1, "fontTex", 1, BlockNames,
|
||||
boo::BlendFactor::SrcColor1, boo::BlendFactor::InvSrcColor1,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
ctx.newShaderPipeline(GLSLVS, GLSLFSSubpixel, 1, "fontTex", 1, BlockNames,
|
||||
boo::BlendFactor::SrcColor1, boo::BlendFactor::InvSrcColor1,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
|
||||
void TextView::Resources::init(boo::ID3DDataFactory* factory, FontCache* fcache)
|
||||
void TextView::Resources::init(boo::ID3DDataFactory::Context& ctx, FontCache* fcache)
|
||||
{
|
||||
m_fcache = fcache;
|
||||
|
||||
|
@ -187,7 +187,7 @@ void TextView::Resources::init(boo::ID3DDataFactory* factory, FontCache* fcache)
|
|||
#endif
|
||||
#if BOO_HAS_METAL
|
||||
|
||||
void TextView::Resources::init(boo::MetalDataFactory* factory, FontCache* fcache)
|
||||
void TextView::Resources::init(boo::MetalDataFactory::Context& ctx, FontCache* fcache)
|
||||
{
|
||||
m_fcache = fcache;
|
||||
|
||||
|
@ -264,7 +264,7 @@ void TextView::Resources::init(boo::MetalDataFactory* factory, FontCache* fcache
|
|||
#endif
|
||||
#if BOO_HAS_VULKAN
|
||||
|
||||
void TextView::Resources::init(boo::VulkanDataFactory* factory, FontCache* fcache)
|
||||
void TextView::Resources::init(boo::VulkanDataFactory::Context& ctx, FontCache* fcache)
|
||||
{
|
||||
m_fcache = fcache;
|
||||
|
||||
|
@ -284,68 +284,73 @@ void TextView::Resources::init(boo::VulkanDataFactory* factory, FontCache* fcach
|
|||
{nullptr, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3},
|
||||
{nullptr, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced}
|
||||
};
|
||||
m_vtxFmt = factory->newVertexFormat(13, vdescs);
|
||||
m_vtxFmt = ctx.newVertexFormat(13, vdescs);
|
||||
|
||||
m_regular =
|
||||
factory->newShaderPipeline(GLSLVS, GLSLFSReg, m_vtxFmt,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
ctx.newShaderPipeline(GLSLVS, GLSLFSReg, m_vtxFmt,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TextView::TextView(ViewResources& res, View& parentView, const FontAtlas& font, Alignment align, size_t capacity)
|
||||
TextView::TextView(ViewResources& res,
|
||||
View& parentView, const FontAtlas& font,
|
||||
Alignment align, size_t capacity)
|
||||
: View(res, parentView),
|
||||
m_capacity(capacity),
|
||||
m_fontAtlas(font),
|
||||
m_align(align)
|
||||
{
|
||||
m_glyphBuf =
|
||||
res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex,
|
||||
sizeof(RenderGlyph), capacity);
|
||||
|
||||
boo::IShaderPipeline* shader;
|
||||
if (font.subpixel())
|
||||
shader = res.m_textRes.m_subpixel;
|
||||
else
|
||||
shader = res.m_textRes.m_regular;
|
||||
|
||||
if (!res.m_textRes.m_vtxFmt)
|
||||
{
|
||||
boo::VertexElementDescriptor vdescs[] =
|
||||
{
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 0},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 1},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 2},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 3},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 0},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 1},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 2},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 3},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 0},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 1},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 2},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced}
|
||||
};
|
||||
m_vtxFmt = res.m_factory->newVertexFormat(13, vdescs);
|
||||
boo::ITexture* texs[] = {m_fontAtlas.texture()};
|
||||
m_shaderBinding = res.m_factory->newShaderDataBinding(shader, m_vtxFmt,
|
||||
nullptr, m_glyphBuf, nullptr, 1,
|
||||
(boo::IGraphicsBuffer**)&m_viewVertBlockBuf,
|
||||
1, texs);
|
||||
}
|
||||
else
|
||||
{
|
||||
boo::ITexture* texs[] = {m_fontAtlas.texture()};
|
||||
m_shaderBinding = res.m_factory->newShaderDataBinding(shader, res.m_textRes.m_vtxFmt,
|
||||
nullptr, m_glyphBuf, nullptr, 1,
|
||||
(boo::IGraphicsBuffer**)&m_viewVertBlockBuf,
|
||||
1, texs);
|
||||
}
|
||||
|
||||
m_glyphs.reserve(capacity);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
|
||||
m_glyphBuf =
|
||||
ctx.newDynamicBuffer(boo::BufferUse::Vertex, sizeof(RenderGlyph), capacity);
|
||||
|
||||
boo::IShaderPipeline* shader;
|
||||
if (font.subpixel())
|
||||
shader = res.m_textRes.m_subpixel;
|
||||
else
|
||||
shader = res.m_textRes.m_regular;
|
||||
|
||||
if (!res.m_textRes.m_vtxFmt)
|
||||
{
|
||||
boo::VertexElementDescriptor vdescs[] =
|
||||
{
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 0},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 1},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 2},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 3},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 0},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 1},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 2},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 3},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 0},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 1},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 2},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3},
|
||||
{m_glyphBuf, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced}
|
||||
};
|
||||
m_vtxFmt = ctx.newVertexFormat(13, vdescs);
|
||||
boo::ITexture* texs[] = {m_fontAtlas.texture()};
|
||||
m_shaderBinding = ctx.newShaderDataBinding(shader, m_vtxFmt,
|
||||
nullptr, m_glyphBuf, nullptr, 1,
|
||||
(boo::IGraphicsBuffer**)&m_viewVertBlockBuf,
|
||||
1, texs);
|
||||
}
|
||||
else
|
||||
{
|
||||
boo::ITexture* texs[] = {m_fontAtlas.texture()};
|
||||
m_shaderBinding = ctx.newShaderDataBinding(shader, res.m_textRes.m_vtxFmt,
|
||||
nullptr, m_glyphBuf, nullptr, 1,
|
||||
(boo::IGraphicsBuffer**)&m_viewVertBlockBuf,
|
||||
1, texs);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
TextView::TextView(ViewResources& res, View& parentView, FontTag font, Alignment align, size_t capacity)
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace specter
|
|||
{
|
||||
static logvisor::Module Log("specter::Space");
|
||||
|
||||
void Toolbar::Resources::init(boo::IGraphicsDataFactory* factory, const IThemeData& theme)
|
||||
void Toolbar::Resources::init(boo::IGraphicsDataFactory::Context& ctx, const IThemeData& theme)
|
||||
{
|
||||
static const zeus::RGBA32 tex[] =
|
||||
{
|
||||
|
@ -18,7 +18,7 @@ void Toolbar::Resources::init(boo::IGraphicsDataFactory* factory, const IThemeDa
|
|||
{{0,0,0,64}},
|
||||
{{0,0,0,64}}
|
||||
};
|
||||
m_shadingTex = factory->newStaticTexture(4, 1, 1, boo::TextureFormat::RGBA8, tex, 16);
|
||||
m_shadingTex = ctx.newStaticTexture(4, 1, 1, boo::TextureFormat::RGBA8, tex, 16);
|
||||
}
|
||||
|
||||
Toolbar::Toolbar(ViewResources& res, View& parentView, Position tbPos, unsigned units)
|
||||
|
@ -29,9 +29,13 @@ Toolbar::Toolbar(ViewResources& res, View& parentView, Position tbPos, unsigned
|
|||
m_children.reserve(units);
|
||||
for (unsigned u=0 ; u<units ; ++u)
|
||||
m_children.emplace_back();
|
||||
m_tbBlockBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1);
|
||||
m_vertsBinding.initTex(res, 10, m_tbBlockBuf, res.m_toolbarRes.m_shadingTex);
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_tbBlockBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1);
|
||||
m_vertsBinding.initTex(ctx, res, 10, m_tbBlockBuf, res.m_toolbarRes.m_shadingTex);
|
||||
return true;
|
||||
});
|
||||
setBackground(res.themeData().toolbarBackground());
|
||||
}
|
||||
|
||||
|
|
|
@ -12,13 +12,16 @@ Tooltip::Tooltip(ViewResources& res, View& parentView, const std::string& title,
|
|||
const std::string& message)
|
||||
: View(res, parentView), m_titleStr(title), m_messageStr(message)
|
||||
{
|
||||
m_ttBlockBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1);
|
||||
m_vertsBinding.initSolid(res, 16, m_ttBlockBuf);
|
||||
|
||||
for (int i=0 ; i<16 ; ++i)
|
||||
m_ttVerts[i].m_color = res.themeData().tooltipBackground();
|
||||
|
||||
commitResources(res);
|
||||
commitResources(res, [&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
buildResources(ctx, res);
|
||||
m_ttBlockBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1);
|
||||
m_vertsBinding.initSolid(ctx, res, 16, m_ttBlockBuf);
|
||||
return true;
|
||||
});
|
||||
|
||||
for (int i=0 ; i<4 ; ++i)
|
||||
{
|
||||
|
|
|
@ -71,17 +71,17 @@ BOO_GLSL_BINDING_HEAD
|
|||
" colorOut = texture(tex, vtf.uv) * vtf.color;\n"
|
||||
"}\n";
|
||||
|
||||
void View::Resources::init(boo::GLDataFactory* factory, const IThemeData& theme)
|
||||
void View::Resources::init(boo::GLDataFactory::Context& ctx, const IThemeData& theme)
|
||||
{
|
||||
static const char* BlockNames[] = {"SpecterViewBlock"};
|
||||
|
||||
m_solidShader = factory->newShaderPipeline(GLSLSolidVS, GLSLSolidFS, 0, nullptr, 1, BlockNames,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
m_solidShader = ctx.newShaderPipeline(GLSLSolidVS, GLSLSolidFS, 0, nullptr, 1, BlockNames,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
|
||||
m_texShader = factory->newShaderPipeline(GLSLTexVS, GLSLTexFS, 1, "tex", 1, BlockNames,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
m_texShader = ctx.newShaderPipeline(GLSLTexVS, GLSLTexFS, 1, "tex", 1, BlockNames,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
|
@ -289,16 +289,16 @@ void View::Resources::init(boo::MetalDataFactory* factory, const IThemeData& the
|
|||
#endif
|
||||
#if BOO_HAS_VULKAN
|
||||
|
||||
void View::Resources::init(boo::VulkanDataFactory* factory, const IThemeData& theme)
|
||||
void View::Resources::init(boo::VulkanDataFactory::Context& ctx, const IThemeData& theme)
|
||||
{
|
||||
boo::VertexElementDescriptor solidvdescs[] =
|
||||
{
|
||||
{nullptr, nullptr, boo::VertexSemantic::Position4},
|
||||
{nullptr, nullptr, boo::VertexSemantic::Color}
|
||||
};
|
||||
m_solidVtxFmt = factory->newVertexFormat(2, solidvdescs);
|
||||
m_solidVtxFmt = ctx.newVertexFormat(2, solidvdescs);
|
||||
|
||||
m_solidShader = factory->newShaderPipeline(GLSLSolidVS, GLSLSolidFS, m_solidVtxFmt,
|
||||
m_solidShader = ctx.newShaderPipeline(GLSLSolidVS, GLSLSolidFS, m_solidVtxFmt,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
|
||||
|
@ -307,36 +307,29 @@ void View::Resources::init(boo::VulkanDataFactory* factory, const IThemeData& th
|
|||
{nullptr, nullptr, boo::VertexSemantic::Position4},
|
||||
{nullptr, nullptr, boo::VertexSemantic::UV4}
|
||||
};
|
||||
m_texVtxFmt = factory->newVertexFormat(2, texvdescs);
|
||||
m_texVtxFmt = ctx.newVertexFormat(2, texvdescs);
|
||||
|
||||
m_texShader = factory->newShaderPipeline(GLSLTexVS, GLSLTexFS, m_texVtxFmt,
|
||||
m_texShader = ctx.newShaderPipeline(GLSLTexVS, GLSLTexFS, m_texVtxFmt,
|
||||
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
|
||||
boo::Primitive::TriStrips, false, false, false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void View::buildResources(ViewResources& res)
|
||||
void View::buildResources(boo::IGraphicsDataFactory::Context& ctx, ViewResources& res)
|
||||
{
|
||||
m_viewVertBlockBuf =
|
||||
res.m_factory->newDynamicBuffer(boo::BufferUse::Uniform,
|
||||
sizeof(ViewBlock), 1);
|
||||
m_bgVertsBinding.initSolid(res, 4, m_viewVertBlockBuf);
|
||||
ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(ViewBlock), 1);
|
||||
m_bgVertsBinding.initSolid(ctx, res, 4, m_viewVertBlockBuf);
|
||||
}
|
||||
|
||||
View::View(ViewResources& res)
|
||||
: m_rootView(*static_cast<RootView*>(this)),
|
||||
m_parentView(*static_cast<RootView*>(this))
|
||||
{
|
||||
buildResources(res);
|
||||
}
|
||||
m_parentView(*static_cast<RootView*>(this)) {}
|
||||
|
||||
View::View(ViewResources& res, View& parentView)
|
||||
: m_rootView(parentView.rootView()),
|
||||
m_parentView(parentView)
|
||||
{
|
||||
buildResources(res);
|
||||
}
|
||||
m_parentView(parentView) {}
|
||||
|
||||
void View::updateSize()
|
||||
{
|
||||
|
@ -351,7 +344,8 @@ void View::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
|||
m_bgRect[1].m_pos.assign(0.f, 0.f, 0.f);
|
||||
m_bgRect[2].m_pos.assign(sub.size[0], sub.size[1], 0.f);
|
||||
m_bgRect[3].m_pos.assign(sub.size[0], 0.f, 0.f);
|
||||
m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(ViewBlock));
|
||||
if (m_viewVertBlockBuf)
|
||||
m_viewVertBlockBuf->load(&m_viewVertBlock, sizeof(ViewBlock));
|
||||
m_bgVertsBinding.load(m_bgRect, sizeof(m_bgRect));
|
||||
}
|
||||
|
||||
|
@ -362,26 +356,32 @@ void View::resized(const ViewBlock& vb, const boo::SWindowRect& sub)
|
|||
m_bgRect[1].m_pos.assign(0.f, 0.f, 0.f);
|
||||
m_bgRect[2].m_pos.assign(sub.size[0], sub.size[1], 0.f);
|
||||
m_bgRect[3].m_pos.assign(sub.size[0], 0.f, 0.f);
|
||||
m_viewVertBlockBuf->load(&vb, sizeof(ViewBlock));
|
||||
if (m_viewVertBlockBuf)
|
||||
m_viewVertBlockBuf->load(&vb, sizeof(ViewBlock));
|
||||
m_bgVertsBinding.load(m_bgRect, sizeof(m_bgRect));
|
||||
}
|
||||
|
||||
void View::draw(boo::IGraphicsCommandQueue* gfxQ)
|
||||
{
|
||||
gfxQ->setShaderDataBinding(m_bgVertsBinding);
|
||||
gfxQ->draw(0, 4);
|
||||
if (m_bgVertsBinding.m_shaderBinding)
|
||||
{
|
||||
gfxQ->setShaderDataBinding(m_bgVertsBinding);
|
||||
gfxQ->draw(0, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void View::commitResources(ViewResources& res)
|
||||
void View::commitResources(ViewResources& res, const boo::FactoryCommitFunc& commitFunc)
|
||||
{
|
||||
if (m_gfxData)
|
||||
Log.report(logvisor::Fatal, "multiple resource commits not allowed");
|
||||
m_gfxData = res.m_factory->commit();
|
||||
m_gfxData = res.m_factory->commitTransaction(commitFunc);
|
||||
}
|
||||
|
||||
void View::VertexBufferBinding::initSolid(ViewResources& res, size_t count, boo::IGraphicsBuffer* viewBlockBuf)
|
||||
void View::VertexBufferBinding::initSolid(boo::IGraphicsDataFactory::Context& ctx,
|
||||
ViewResources& res, size_t count,
|
||||
boo::IGraphicsBuffer* viewBlockBuf)
|
||||
{
|
||||
m_vertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(SolidShaderVert), count);
|
||||
m_vertsBuf = ctx.newDynamicBuffer(boo::BufferUse::Vertex, sizeof(SolidShaderVert), count);
|
||||
|
||||
if (!res.m_viewRes.m_solidVtxFmt)
|
||||
{
|
||||
|
@ -390,25 +390,28 @@ void View::VertexBufferBinding::initSolid(ViewResources& res, size_t count, boo:
|
|||
{m_vertsBuf, nullptr, boo::VertexSemantic::Position4},
|
||||
{m_vertsBuf, nullptr, boo::VertexSemantic::Color}
|
||||
};
|
||||
m_vtxFmt = res.m_factory->newVertexFormat(2, vdescs);
|
||||
m_vtxFmt = ctx.newVertexFormat(2, vdescs);
|
||||
boo::IGraphicsBuffer* bufs[] = {viewBlockBuf};
|
||||
m_shaderBinding = res.m_factory->newShaderDataBinding(res.m_viewRes.m_solidShader,
|
||||
m_vtxFmt, m_vertsBuf, nullptr,
|
||||
nullptr, 1, bufs, 0, nullptr);
|
||||
m_shaderBinding = ctx.newShaderDataBinding(res.m_viewRes.m_solidShader,
|
||||
m_vtxFmt, m_vertsBuf, nullptr,
|
||||
nullptr, 1, bufs, 0, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
boo::IGraphicsBuffer* bufs[] = {viewBlockBuf};
|
||||
m_shaderBinding = res.m_factory->newShaderDataBinding(res.m_viewRes.m_solidShader,
|
||||
res.m_viewRes.m_solidVtxFmt,
|
||||
m_vertsBuf, nullptr,
|
||||
nullptr, 1, bufs, 0, nullptr);
|
||||
m_shaderBinding = ctx.newShaderDataBinding(res.m_viewRes.m_solidShader,
|
||||
res.m_viewRes.m_solidVtxFmt,
|
||||
m_vertsBuf, nullptr,
|
||||
nullptr, 1, bufs, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void View::VertexBufferBinding::initTex(ViewResources& res, size_t count, boo::IGraphicsBuffer* viewBlockBuf, boo::ITexture* texture)
|
||||
void View::VertexBufferBinding::initTex(boo::IGraphicsDataFactory::Context& ctx,
|
||||
ViewResources& res, size_t count,
|
||||
boo::IGraphicsBuffer* viewBlockBuf,
|
||||
boo::ITexture* texture)
|
||||
{
|
||||
m_vertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(TexShaderVert), count);
|
||||
m_vertsBuf = ctx.newDynamicBuffer(boo::BufferUse::Vertex, sizeof(TexShaderVert), count);
|
||||
|
||||
if (!res.m_viewRes.m_texVtxFmt)
|
||||
{
|
||||
|
@ -417,21 +420,21 @@ void View::VertexBufferBinding::initTex(ViewResources& res, size_t count, boo::I
|
|||
{m_vertsBuf, nullptr, boo::VertexSemantic::Position4},
|
||||
{m_vertsBuf, nullptr, boo::VertexSemantic::UV4}
|
||||
};
|
||||
m_vtxFmt = res.m_factory->newVertexFormat(2, vdescs);
|
||||
m_vtxFmt = ctx.newVertexFormat(2, vdescs);
|
||||
boo::IGraphicsBuffer* bufs[] = {viewBlockBuf};
|
||||
boo::ITexture* tex[] = {texture};
|
||||
m_shaderBinding = res.m_factory->newShaderDataBinding(res.m_viewRes.m_texShader,
|
||||
m_vtxFmt, m_vertsBuf, nullptr,
|
||||
nullptr, 1, bufs, 1, tex);
|
||||
m_shaderBinding = ctx.newShaderDataBinding(res.m_viewRes.m_texShader,
|
||||
m_vtxFmt, m_vertsBuf, nullptr,
|
||||
nullptr, 1, bufs, 1, tex);
|
||||
}
|
||||
else
|
||||
{
|
||||
boo::IGraphicsBuffer* bufs[] = {viewBlockBuf};
|
||||
boo::ITexture* tex[] = {texture};
|
||||
m_shaderBinding = res.m_factory->newShaderDataBinding(res.m_viewRes.m_texShader,
|
||||
res.m_viewRes.m_texVtxFmt,
|
||||
m_vertsBuf, nullptr,
|
||||
nullptr, 1, bufs, 1, tex);
|
||||
m_shaderBinding = ctx.newShaderDataBinding(res.m_viewRes.m_texShader,
|
||||
res.m_viewRes.m_texVtxFmt,
|
||||
m_vertsBuf, nullptr,
|
||||
nullptr, 1, bufs, 1, tex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,36 +10,42 @@ void ViewResources::init(boo::IGraphicsDataFactory* factory, FontCache* fcache,
|
|||
if (!factory || !fcache || !theme)
|
||||
Log.report(logvisor::Fatal, "all arguments of ViewResources::init() must be non-null");
|
||||
m_pixelFactor = pf;
|
||||
m_theme = theme;
|
||||
m_factory = factory;
|
||||
m_theme = theme;
|
||||
m_fcache = fcache;
|
||||
unsigned dpi = 72.f * m_pixelFactor;
|
||||
m_curveFont = fcache->prepCurvesFont(m_factory, AllCharFilter, false, 8.f, dpi);
|
||||
switch (factory->platform())
|
||||
|
||||
m_curveFont = fcache->prepCurvesFont(factory, AllCharFilter, false, 8.f, dpi);
|
||||
|
||||
m_resData = factory->commitTransaction(
|
||||
[&](boo::IGraphicsDataFactory::Context& ctx) -> bool
|
||||
{
|
||||
case boo::IGraphicsDataFactory::Platform::OGL:
|
||||
init<boo::GLDataFactory>(static_cast<boo::GLDataFactory*>(factory), *theme, fcache);
|
||||
break;
|
||||
switch (ctx.platform())
|
||||
{
|
||||
case boo::IGraphicsDataFactory::Platform::OGL:
|
||||
init<boo::GLDataFactory::Context>(static_cast<boo::GLDataFactory::Context&>(ctx), *theme, fcache);
|
||||
break;
|
||||
#if _WIN32
|
||||
case boo::IGraphicsDataFactory::Platform::D3D11:
|
||||
case boo::IGraphicsDataFactory::Platform::D3D12:
|
||||
init<boo::ID3DDataFactory>(static_cast<boo::ID3DDataFactory*>(factory), *theme, fcache);
|
||||
break;
|
||||
case boo::IGraphicsDataFactory::Platform::D3D11:
|
||||
case boo::IGraphicsDataFactory::Platform::D3D12:
|
||||
init<boo::ID3DDataFactory::Context>(static_cast<boo::ID3DDataFactory::Context&>(ctx), *theme, fcache);
|
||||
break;
|
||||
#endif
|
||||
#if BOO_HAS_METAL
|
||||
case boo::IGraphicsDataFactory::Platform::Metal:
|
||||
init<boo::MetalDataFactory>(static_cast<boo::MetalDataFactory*>(factory), *theme, fcache);
|
||||
break;
|
||||
case boo::IGraphicsDataFactory::Platform::Metal:
|
||||
init<boo::MetalDataFactory::Context>(static_cast<boo::MetalDataFactory::Context&>(ctx), *theme, fcache);
|
||||
break;
|
||||
#endif
|
||||
#if BOO_HAS_VULKAN
|
||||
case boo::IGraphicsDataFactory::Platform::Vulkan:
|
||||
init<boo::VulkanDataFactory>(static_cast<boo::VulkanDataFactory*>(factory), *theme, fcache);
|
||||
break;
|
||||
case boo::IGraphicsDataFactory::Platform::Vulkan:
|
||||
init<boo::VulkanDataFactory::Context>(static_cast<boo::VulkanDataFactory::Context&>(ctx), *theme, fcache);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
Log.report(logvisor::Fatal, _S("unable to init view system for %s"), factory->platformName());
|
||||
}
|
||||
m_resData = factory->commit();
|
||||
default:
|
||||
Log.report(logvisor::Fatal, _S("unable to init view system for %s"), ctx.platformName());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void ViewResources::prepFontCacheSync()
|
||||
|
|
Loading…
Reference in New Issue