mirror of https://github.com/AxioDL/metaforce.git
More Menu work
This commit is contained in:
parent
531dafdc68
commit
8697291405
|
@ -81,6 +81,7 @@ public:
|
||||||
void init(boo::IGraphicsDataFactory* factory, const IThemeData& theme);
|
void init(boo::IGraphicsDataFactory* factory, const IThemeData& theme);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
~Button() {closeMenu({});}
|
||||||
Button(ViewResources& res, View& parentView,
|
Button(ViewResources& res, View& parentView,
|
||||||
IButtonBinding* controlBinding, const std::string& text,
|
IButtonBinding* controlBinding, const std::string& text,
|
||||||
Style style=Style::Block, RectangleConstraint constraint=RectangleConstraint());
|
Style style=Style::Block, RectangleConstraint constraint=RectangleConstraint());
|
||||||
|
@ -92,6 +93,7 @@ public:
|
||||||
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||||
void mouseMove(const boo::SWindowCoord&);
|
void mouseMove(const boo::SWindowCoord&);
|
||||||
void mouseLeave(const boo::SWindowCoord&);
|
void mouseLeave(const boo::SWindowCoord&);
|
||||||
|
void think();
|
||||||
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
|
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
|
||||||
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||||
|
|
||||||
|
@ -102,6 +104,9 @@ public:
|
||||||
int nominalWidth() const {return m_nomWidth;}
|
int nominalWidth() const {return m_nomWidth;}
|
||||||
int nominalHeight() const {return m_nomHeight;}
|
int nominalHeight() const {return m_nomHeight;}
|
||||||
|
|
||||||
|
void closeMenu(const boo::SWindowCoord& coord);
|
||||||
|
ViewChild<std::unique_ptr<View>>& getMenu() {return m_modalMenu;}
|
||||||
|
|
||||||
void setMultiplyColor(const Zeus::CColor& color)
|
void setMultiplyColor(const Zeus::CColor& color)
|
||||||
{
|
{
|
||||||
View::setMultiplyColor(color);
|
View::setMultiplyColor(color);
|
||||||
|
|
|
@ -24,22 +24,40 @@ class Menu : public View
|
||||||
std::unique_ptr<Menu> m_subMenu;
|
std::unique_ptr<Menu> m_subMenu;
|
||||||
std::unique_ptr<TextView> m_headText;
|
std::unique_ptr<TextView> m_headText;
|
||||||
|
|
||||||
int m_cWidth, m_cHeight;
|
int m_cWidth, m_cHeight, m_cTop;
|
||||||
|
|
||||||
SolidShaderVert m_verts[16];
|
SolidShaderVert m_verts[8];
|
||||||
VertexBufferBinding m_vertsBinding;
|
VertexBufferBinding m_vertsBinding;
|
||||||
|
void setVerts(int width, int height);
|
||||||
|
|
||||||
struct ContentView : View
|
struct ContentView : View
|
||||||
{
|
{
|
||||||
ContentView(ViewResources& res, Menu& menu) : View(res, menu) {}
|
Menu& m_menu;
|
||||||
|
ContentView(ViewResources& res, Menu& menu);
|
||||||
|
|
||||||
|
boo::SWindowRect m_scissorRect;
|
||||||
|
SolidShaderVert m_hlVerts[4];
|
||||||
|
VertexBufferBinding m_hlVertsBinding;
|
||||||
|
|
||||||
|
size_t m_highlightedItem = -1;
|
||||||
|
void setHighlightedItem(size_t idx);
|
||||||
|
void unsetHighlightedItem(size_t idx)
|
||||||
|
{
|
||||||
|
if (m_highlightedItem == idx)
|
||||||
|
setHighlightedItem(-1);
|
||||||
|
}
|
||||||
|
|
||||||
void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||||
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||||
void mouseMove(const boo::SWindowCoord&);
|
void mouseMove(const boo::SWindowCoord&);
|
||||||
void mouseLeave(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);
|
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||||
|
|
||||||
|
int nominalWidth() const {return m_menu.m_cWidth;}
|
||||||
|
int nominalHeight() const {return m_menu.m_cHeight;}
|
||||||
};
|
};
|
||||||
std::unique_ptr<ContentView> m_content;
|
std::unique_ptr<ContentView> m_content;
|
||||||
ViewChild<std::unique_ptr<ScrollView>> m_scroll;
|
ViewChild<std::unique_ptr<ScrollView>> m_scroll;
|
||||||
|
@ -48,12 +66,16 @@ class Menu : public View
|
||||||
{
|
{
|
||||||
Menu& m_menu;
|
Menu& m_menu;
|
||||||
std::unique_ptr<TextView> m_textView;
|
std::unique_ptr<TextView> m_textView;
|
||||||
ItemView(ViewResources& res, Menu& menu, const std::string& text);
|
size_t m_idx;
|
||||||
|
ItemView(ViewResources& res, Menu& menu, const std::string& text, size_t idx);
|
||||||
|
|
||||||
void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||||
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
|
||||||
void mouseEnter(const boo::SWindowCoord&);
|
void mouseEnter(const boo::SWindowCoord&);
|
||||||
void mouseLeave(const boo::SWindowCoord&);
|
void mouseLeave(const boo::SWindowCoord&);
|
||||||
|
|
||||||
|
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
|
||||||
|
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||||
};
|
};
|
||||||
std::vector<ViewChild<std::unique_ptr<ItemView>>> m_items;
|
std::vector<ViewChild<std::unique_ptr<ItemView>>> m_items;
|
||||||
|
|
||||||
|
@ -69,6 +91,7 @@ public:
|
||||||
void mouseLeave(const boo::SWindowCoord&);
|
void mouseLeave(const boo::SWindowCoord&);
|
||||||
void scroll(const boo::SWindowCoord&, const boo::SScrollDelta&);
|
void scroll(const boo::SWindowCoord&, const boo::SScrollDelta&);
|
||||||
|
|
||||||
|
void think();
|
||||||
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
|
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
|
||||||
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
void draw(boo::IGraphicsCommandQueue* gfxQ);
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,6 +26,7 @@ class RootView : public View
|
||||||
ViewResources* m_viewRes;
|
ViewResources* m_viewRes;
|
||||||
ITextInputView* m_activeTextView = nullptr;
|
ITextInputView* m_activeTextView = nullptr;
|
||||||
View* m_activeDragView = nullptr;
|
View* m_activeDragView = nullptr;
|
||||||
|
Button* m_activeMenuButton = nullptr;
|
||||||
|
|
||||||
SplitView* m_hoverSplitDragView = nullptr;
|
SplitView* m_hoverSplitDragView = nullptr;
|
||||||
bool m_activeSplitDragView = false;
|
bool m_activeSplitDragView = false;
|
||||||
|
@ -82,6 +83,15 @@ public:
|
||||||
{
|
{
|
||||||
m_activeDragView = dragView;
|
m_activeDragView = dragView;
|
||||||
}
|
}
|
||||||
|
void setActiveMenuButton(Button* button)
|
||||||
|
{
|
||||||
|
m_activeMenuButton = button;
|
||||||
|
}
|
||||||
|
void unsetActiveMenuButton(Button* button)
|
||||||
|
{
|
||||||
|
if (button == m_activeMenuButton)
|
||||||
|
m_activeMenuButton = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void startSplitDrag(SplitView* sv, const boo::SWindowCoord& coord)
|
void startSplitDrag(SplitView* sv, const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,6 +38,12 @@ Button::Button(ViewResources& res, View& parentView,
|
||||||
m_verts[4].m_color = res.themeData().button2Inactive();
|
m_verts[4].m_color = res.themeData().button2Inactive();
|
||||||
for (int i=5 ; i<28 ; ++i)
|
for (int i=5 ; i<28 ; ++i)
|
||||||
m_verts[i].m_color = res.themeData().button2Inactive();
|
m_verts[i].m_color = res.themeData().button2Inactive();
|
||||||
|
m_verts[31].m_color = rootView().themeData().button1Inactive();
|
||||||
|
m_verts[32].m_color = rootView().themeData().button2Inactive();
|
||||||
|
m_verts[33].m_color = rootView().themeData().button1Inactive();
|
||||||
|
m_verts[34].m_color = rootView().themeData().button2Inactive();
|
||||||
|
for (int i=35 ; i<39 ; ++i)
|
||||||
|
m_verts[i].m_color = res.themeData().button2Inactive();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -82,6 +88,10 @@ void Button::setText(const std::string& text, const Zeus::CColor& textColor)
|
||||||
m_verts[3].m_pos.assign(width+1, 1, 0);
|
m_verts[3].m_pos.assign(width+1, 1, 0);
|
||||||
m_verts[4].m_pos.assign(width+1, 1, 0);
|
m_verts[4].m_pos.assign(width+1, 1, 0);
|
||||||
|
|
||||||
|
m_textWidth = width;
|
||||||
|
if (m_menuStyle != IButtonBinding::MenuStyle::None)
|
||||||
|
width += 16*pf;
|
||||||
|
|
||||||
m_verts[5].m_pos.assign(1, height+1, 0);
|
m_verts[5].m_pos.assign(1, height+1, 0);
|
||||||
m_verts[6].m_pos.assign(1, height+1, 0);
|
m_verts[6].m_pos.assign(1, height+1, 0);
|
||||||
m_verts[7].m_pos.assign(0, height+1, 0);
|
m_verts[7].m_pos.assign(0, height+1, 0);
|
||||||
|
@ -109,7 +119,21 @@ void Button::setText(const std::string& text, const Zeus::CColor& textColor)
|
||||||
m_verts[26].m_pos.assign(width+1, 1, 0);
|
m_verts[26].m_pos.assign(width+1, 1, 0);
|
||||||
m_verts[27].m_pos.assign(width+1, 0, 0);
|
m_verts[27].m_pos.assign(width+1, 0, 0);
|
||||||
|
|
||||||
m_textWidth = width;
|
int arrowX = m_textWidth + 5*pf;
|
||||||
|
int arrowY = 7*pf;
|
||||||
|
m_verts[28].m_pos.assign(arrowX + 4*pf, arrowY + 1*pf, 0);
|
||||||
|
m_verts[29].m_pos.assign(arrowX, arrowY + 5*pf, 0);
|
||||||
|
m_verts[30].m_pos.assign(arrowX + 8*pf, arrowY + 5*pf, 0);
|
||||||
|
|
||||||
|
m_verts[31].m_pos.assign(m_textWidth+1, height+1, 0);
|
||||||
|
m_verts[32].m_pos.assign(m_textWidth+1, 1, 0);
|
||||||
|
m_verts[33].m_pos.assign(width+1, height+1, 0);
|
||||||
|
m_verts[34].m_pos.assign(width+1, 1, 0);
|
||||||
|
|
||||||
|
m_verts[35].m_pos.assign(m_textWidth, height+1, 0);
|
||||||
|
m_verts[36].m_pos.assign(m_textWidth, 1, 0);
|
||||||
|
m_verts[37].m_pos.assign(m_textWidth+1, height+1, 0);
|
||||||
|
m_verts[38].m_pos.assign(m_textWidth+1, 1, 0);
|
||||||
|
|
||||||
m_vertsBinding.load(m_verts, sizeof(m_verts));
|
m_vertsBinding.load(m_verts, sizeof(m_verts));
|
||||||
}
|
}
|
||||||
|
@ -182,11 +206,10 @@ void Button::MenuTarget::setInactive()
|
||||||
{
|
{
|
||||||
if (m_button.m_style == Style::Block)
|
if (m_button.m_style == Style::Block)
|
||||||
{
|
{
|
||||||
m_button.m_verts[28].m_color = rootView().themeData().button1Inactive();
|
m_button.m_verts[31].m_color = rootView().themeData().button1Inactive();
|
||||||
m_button.m_verts[29].m_color = rootView().themeData().button2Inactive();
|
|
||||||
m_button.m_verts[30].m_color = rootView().themeData().button1Inactive();
|
|
||||||
m_button.m_verts[31].m_color = rootView().themeData().button2Inactive();
|
|
||||||
m_button.m_verts[32].m_color = rootView().themeData().button2Inactive();
|
m_button.m_verts[32].m_color = rootView().themeData().button2Inactive();
|
||||||
|
m_button.m_verts[33].m_color = rootView().themeData().button1Inactive();
|
||||||
|
m_button.m_verts[34].m_color = rootView().themeData().button2Inactive();
|
||||||
m_button.m_vertsBinding.load(m_button.m_verts, sizeof(m_button.m_verts));
|
m_button.m_vertsBinding.load(m_button.m_verts, sizeof(m_button.m_verts));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -223,11 +246,10 @@ void Button::MenuTarget::setHover()
|
||||||
{
|
{
|
||||||
if (m_button.m_style == Style::Block)
|
if (m_button.m_style == Style::Block)
|
||||||
{
|
{
|
||||||
m_button.m_verts[28].m_color = rootView().themeData().button1Hover();
|
m_button.m_verts[31].m_color = rootView().themeData().button1Hover();
|
||||||
m_button.m_verts[29].m_color = rootView().themeData().button2Hover();
|
|
||||||
m_button.m_verts[30].m_color = rootView().themeData().button1Hover();
|
|
||||||
m_button.m_verts[31].m_color = rootView().themeData().button2Hover();
|
|
||||||
m_button.m_verts[32].m_color = rootView().themeData().button2Hover();
|
m_button.m_verts[32].m_color = rootView().themeData().button2Hover();
|
||||||
|
m_button.m_verts[33].m_color = rootView().themeData().button1Hover();
|
||||||
|
m_button.m_verts[34].m_color = rootView().themeData().button2Hover();
|
||||||
m_button.m_vertsBinding.load(m_button.m_verts, sizeof(m_button.m_verts));
|
m_button.m_vertsBinding.load(m_button.m_verts, sizeof(m_button.m_verts));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -264,11 +286,10 @@ void Button::MenuTarget::setPressed()
|
||||||
{
|
{
|
||||||
if (m_button.m_style == Style::Block)
|
if (m_button.m_style == Style::Block)
|
||||||
{
|
{
|
||||||
m_button.m_verts[28].m_color = rootView().themeData().button1Press();
|
m_button.m_verts[31].m_color = rootView().themeData().button1Press();
|
||||||
m_button.m_verts[29].m_color = rootView().themeData().button2Press();
|
|
||||||
m_button.m_verts[30].m_color = rootView().themeData().button1Press();
|
|
||||||
m_button.m_verts[31].m_color = rootView().themeData().button2Press();
|
|
||||||
m_button.m_verts[32].m_color = rootView().themeData().button2Press();
|
m_button.m_verts[32].m_color = rootView().themeData().button2Press();
|
||||||
|
m_button.m_verts[33].m_color = rootView().themeData().button1Press();
|
||||||
|
m_button.m_verts[34].m_color = rootView().themeData().button2Press();
|
||||||
m_button.m_vertsBinding.load(m_button.m_verts, sizeof(m_button.m_verts));
|
m_button.m_vertsBinding.load(m_button.m_verts, sizeof(m_button.m_verts));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -307,11 +328,10 @@ void Button::MenuTarget::setDisabled()
|
||||||
{
|
{
|
||||||
if (m_button.m_style == Style::Block)
|
if (m_button.m_style == Style::Block)
|
||||||
{
|
{
|
||||||
m_button.m_verts[28].m_color = rootView().themeData().button1Disabled();
|
m_button.m_verts[31].m_color = rootView().themeData().button1Disabled();
|
||||||
m_button.m_verts[29].m_color = rootView().themeData().button2Disabled();
|
|
||||||
m_button.m_verts[30].m_color = rootView().themeData().button1Disabled();
|
|
||||||
m_button.m_verts[31].m_color = rootView().themeData().button2Disabled();
|
|
||||||
m_button.m_verts[32].m_color = rootView().themeData().button2Disabled();
|
m_button.m_verts[32].m_color = rootView().themeData().button2Disabled();
|
||||||
|
m_button.m_verts[33].m_color = rootView().themeData().button1Disabled();
|
||||||
|
m_button.m_verts[34].m_color = rootView().themeData().button2Disabled();
|
||||||
m_button.m_vertsBinding.load(m_button.m_verts, sizeof(m_button.m_verts));
|
m_button.m_vertsBinding.load(m_button.m_verts, sizeof(m_button.m_verts));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -351,7 +371,8 @@ void Button::MenuTarget::mouseDown(const boo::SWindowCoord& coord, boo::EMouseBu
|
||||||
if (m_button.m_controlBinding)
|
if (m_button.m_controlBinding)
|
||||||
{
|
{
|
||||||
m_button.m_modalMenu.m_view = static_cast<IButtonBinding&>(*m_button.m_controlBinding).buildMenu(&m_button);
|
m_button.m_modalMenu.m_view = static_cast<IButtonBinding&>(*m_button.m_controlBinding).buildMenu(&m_button);
|
||||||
updateSize();
|
rootView().setActiveMenuButton(&m_button);
|
||||||
|
m_button.updateSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,6 +457,19 @@ void Button::MenuTarget::mouseLeave(const boo::SWindowCoord& coord)
|
||||||
setInactive();
|
setInactive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Button::closeMenu(const boo::SWindowCoord& coord)
|
||||||
|
{
|
||||||
|
rootView().unsetActiveMenuButton(this);
|
||||||
|
m_modalMenu.m_view.reset();
|
||||||
|
m_menuTarget.mouseMove(coord);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::think()
|
||||||
|
{
|
||||||
|
if (m_modalMenu.m_view)
|
||||||
|
m_modalMenu.m_view->think();
|
||||||
|
}
|
||||||
|
|
||||||
void Button::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
void Button::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
||||||
{
|
{
|
||||||
View::resized(root, sub);
|
View::resized(root, sub);
|
||||||
|
@ -449,9 +483,25 @@ void Button::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
||||||
m_text->resized(root, textRect);
|
m_text->resized(root, textRect);
|
||||||
|
|
||||||
if (m_style == Style::Block)
|
if (m_style == Style::Block)
|
||||||
|
{
|
||||||
|
if (m_menuStyle == IButtonBinding::MenuStyle::None)
|
||||||
{
|
{
|
||||||
m_buttonTarget.m_view->resized(root, sub);
|
m_buttonTarget.m_view->resized(root, sub);
|
||||||
}
|
}
|
||||||
|
else if (m_menuStyle == IButtonBinding::MenuStyle::Primary)
|
||||||
|
{
|
||||||
|
m_menuTarget.m_view->resized(root, sub);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boo::SWindowRect targetRect = sub;
|
||||||
|
targetRect.size[0] = m_textWidth;
|
||||||
|
m_buttonTarget.m_view->resized(root, targetRect);
|
||||||
|
targetRect.location[0] += targetRect.size[0];
|
||||||
|
targetRect.size[0] = 16*pf;
|
||||||
|
m_menuTarget.m_view->resized(root, targetRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_menuStyle == IButtonBinding::MenuStyle::Primary)
|
if (m_menuStyle == IButtonBinding::MenuStyle::Primary)
|
||||||
|
@ -474,7 +524,12 @@ void Button::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_modalMenu.m_view)
|
if (m_modalMenu.m_view)
|
||||||
m_modalMenu.m_view->resized(root, sub);
|
{
|
||||||
|
boo::SWindowRect menuRect = sub;
|
||||||
|
if (m_style == Style::Text)
|
||||||
|
menuRect.location[1] -= 6*pf;
|
||||||
|
m_modalMenu.m_view->resized(root, menuRect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::draw(boo::IGraphicsCommandQueue* gfxQ)
|
void Button::draw(boo::IGraphicsCommandQueue* gfxQ)
|
||||||
|
@ -483,7 +538,15 @@ void Button::draw(boo::IGraphicsCommandQueue* gfxQ)
|
||||||
gfxQ->setShaderDataBinding(m_vertsBinding);
|
gfxQ->setShaderDataBinding(m_vertsBinding);
|
||||||
gfxQ->setDrawPrimitive(boo::Primitive::TriStrips);
|
gfxQ->setDrawPrimitive(boo::Primitive::TriStrips);
|
||||||
if (m_style == Style::Block)
|
if (m_style == Style::Block)
|
||||||
|
{
|
||||||
gfxQ->draw(0, 28);
|
gfxQ->draw(0, 28);
|
||||||
|
if (m_menuStyle != IButtonBinding::MenuStyle::None)
|
||||||
|
{
|
||||||
|
gfxQ->draw(31, 4);
|
||||||
|
gfxQ->draw(28, 3);
|
||||||
|
gfxQ->draw(35, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gfxQ->draw(0, 4);
|
gfxQ->draw(0, 4);
|
||||||
|
|
|
@ -2,13 +2,16 @@
|
||||||
#include "Specter/RootView.hpp"
|
#include "Specter/RootView.hpp"
|
||||||
#include "Specter/ViewResources.hpp"
|
#include "Specter/ViewResources.hpp"
|
||||||
|
|
||||||
|
#define ROW_HEIGHT 18
|
||||||
|
#define ITEM_MARGIN 1
|
||||||
|
|
||||||
namespace Specter
|
namespace Specter
|
||||||
{
|
{
|
||||||
|
|
||||||
Menu::Menu(ViewResources& res, View& parentView, IMenuNode* rootNode)
|
Menu::Menu(ViewResources& res, View& parentView, IMenuNode* rootNode)
|
||||||
: View(res, parentView)
|
: View(res, parentView)
|
||||||
{
|
{
|
||||||
m_vertsBinding.initSolid(res, 16, m_viewVertBlockBuf);
|
m_vertsBinding.initSolid(res, 8, m_viewVertBlockBuf);
|
||||||
commitResources(res);
|
commitResources(res);
|
||||||
m_headText.reset(new TextView(res, *this, res.m_mainFont));
|
m_headText.reset(new TextView(res, *this, res.m_mainFont));
|
||||||
m_scroll.m_view.reset(new ScrollView(res, *this, ScrollView::Style::ThinIndicator));
|
m_scroll.m_view.reset(new ScrollView(res, *this, ScrollView::Style::ThinIndicator));
|
||||||
|
@ -23,10 +26,9 @@ void Menu::reset(IMenuNode* rootNode)
|
||||||
m_thisNode = rootNode;
|
m_thisNode = rootNode;
|
||||||
ViewResources& res = rootView().viewRes();
|
ViewResources& res = rootView().viewRes();
|
||||||
|
|
||||||
for (int i=0 ; i<16 ; ++i)
|
for (int i=0 ; i<8 ; ++i)
|
||||||
m_verts[i].m_color = res.themeData().tooltipBackground();
|
m_verts[i].m_color = res.themeData().tooltipBackground();
|
||||||
m_vertsBinding.load(m_verts, sizeof(m_verts));
|
m_vertsBinding.load(m_verts, sizeof(m_verts));
|
||||||
setBackground(Zeus::CColor::skBlue);
|
|
||||||
|
|
||||||
m_subMenu.reset();
|
m_subMenu.reset();
|
||||||
|
|
||||||
|
@ -34,8 +36,10 @@ void Menu::reset(IMenuNode* rootNode)
|
||||||
m_headText->typesetGlyphs(headText?*headText:"", rootView().themeData().uiText());
|
m_headText->typesetGlyphs(headText?*headText:"", rootView().themeData().uiText());
|
||||||
|
|
||||||
float pf = rootView().viewRes().pixelFactor();
|
float pf = rootView().viewRes().pixelFactor();
|
||||||
|
int itemAdv = (ROW_HEIGHT + ITEM_MARGIN*2) * pf;
|
||||||
m_cWidth = m_headText->nominalWidth() + 10*pf;
|
m_cWidth = m_headText->nominalWidth() + 10*pf;
|
||||||
m_cHeight = 22*pf;
|
m_cHeight = headText ? itemAdv : 0;
|
||||||
|
m_cTop = m_cHeight;
|
||||||
|
|
||||||
size_t subCount = rootNode->subNodeCount();
|
size_t subCount = rootNode->subNodeCount();
|
||||||
m_items.clear();
|
m_items.clear();
|
||||||
|
@ -52,11 +56,11 @@ void Menu::reset(IMenuNode* rootNode)
|
||||||
|
|
||||||
if (nodeText)
|
if (nodeText)
|
||||||
{
|
{
|
||||||
item.m_view.reset(new ItemView(res, *this, *nodeText));
|
item.m_view.reset(new ItemView(res, *this, *nodeText, i));
|
||||||
m_cWidth = std::max(m_cWidth, int(item.m_view->m_textView->nominalWidth() + 10*pf));
|
m_cWidth = std::max(m_cWidth, int(item.m_view->m_textView->nominalWidth() + 10*pf));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cHeight += 22*pf;
|
m_cHeight += itemAdv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +68,7 @@ void Menu::reset(IMenuNode* rootNode)
|
||||||
Menu::Menu(ViewResources& res, View& parentView, IMenuNode* rootNode, IMenuNode* thisNode)
|
Menu::Menu(ViewResources& res, View& parentView, IMenuNode* rootNode, IMenuNode* thisNode)
|
||||||
: View(res, parentView), m_rootNode(rootNode), m_thisNode(thisNode)
|
: View(res, parentView), m_rootNode(rootNode), m_thisNode(thisNode)
|
||||||
{
|
{
|
||||||
m_vertsBinding.initSolid(res, 16, m_viewVertBlockBuf);
|
m_vertsBinding.initSolid(res, 8, m_viewVertBlockBuf);
|
||||||
commitResources(res);
|
commitResources(res);
|
||||||
m_headText.reset(new TextView(res, *this, res.m_mainFont));
|
m_headText.reset(new TextView(res, *this, res.m_mainFont));
|
||||||
m_scroll.m_view.reset(new ScrollView(res, *this, ScrollView::Style::ThinIndicator));
|
m_scroll.m_view.reset(new ScrollView(res, *this, ScrollView::Style::ThinIndicator));
|
||||||
|
@ -72,22 +76,78 @@ Menu::Menu(ViewResources& res, View& parentView, IMenuNode* rootNode, IMenuNode*
|
||||||
m_scroll.m_view->setContentView(m_content.get());
|
m_scroll.m_view->setContentView(m_content.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu::ItemView::ItemView(ViewResources& res, Menu& menu, const std::string& text)
|
Menu::ContentView::ContentView(ViewResources& res, Menu& menu)
|
||||||
: View(res, menu), m_menu(menu)
|
: View(res, menu), m_menu(menu)
|
||||||
|
{
|
||||||
|
m_hlVertsBinding.initSolid(res, 4, m_viewVertBlockBuf);
|
||||||
|
commitResources(res);
|
||||||
|
|
||||||
|
m_hlVerts[0].m_color = res.themeData().button1Hover();
|
||||||
|
m_hlVerts[1].m_color = res.themeData().button2Hover();
|
||||||
|
m_hlVerts[2].m_color = res.themeData().button1Hover();
|
||||||
|
m_hlVerts[3].m_color = res.themeData().button2Hover();
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu::ItemView::ItemView(ViewResources& res, Menu& menu, const std::string& text, size_t idx)
|
||||||
|
: View(res, menu), m_menu(menu), m_idx(idx)
|
||||||
{
|
{
|
||||||
commitResources(res);
|
commitResources(res);
|
||||||
m_textView.reset(new Specter::TextView(res, *this, res.m_mainFont));
|
m_textView.reset(new Specter::TextView(res, *this, res.m_mainFont));
|
||||||
m_textView->typesetGlyphs(text, res.themeData().uiText());
|
m_textView->typesetGlyphs(text, res.themeData().uiText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::setVerts(int width, int height)
|
||||||
|
{
|
||||||
|
m_verts[0].m_pos.assign(0, height-m_cTop, 0);
|
||||||
|
m_verts[1].m_pos.assign(0, 0, 0);
|
||||||
|
m_verts[2].m_pos.assign(width, height-m_cTop, 0);
|
||||||
|
m_verts[3].m_pos.assign(width, 0, 0);
|
||||||
|
|
||||||
|
m_verts[4].m_pos.assign(0, height, 0);
|
||||||
|
m_verts[5].m_pos.assign(0, height-m_cTop, 0);
|
||||||
|
m_verts[6].m_pos.assign(width, height, 0);
|
||||||
|
m_verts[7].m_pos.assign(width, height-m_cTop, 0);
|
||||||
|
|
||||||
|
m_vertsBinding.load(m_verts, sizeof(m_verts));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::ContentView::setHighlightedItem(size_t idx)
|
||||||
|
{
|
||||||
|
if (idx == -1)
|
||||||
|
{
|
||||||
|
m_highlightedItem = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ViewChild<std::unique_ptr<ItemView>>& vc = m_menu.m_items[idx];
|
||||||
|
|
||||||
|
if (!vc.m_view)
|
||||||
|
{
|
||||||
|
m_highlightedItem = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_highlightedItem = idx;
|
||||||
|
const boo::SWindowRect& bgRect = subRect();
|
||||||
|
const boo::SWindowRect& itemRect = vc.m_view->subRect();
|
||||||
|
int y = itemRect.location[1] - bgRect.location[1];
|
||||||
|
|
||||||
|
m_hlVerts[0].m_pos.assign(0, y+itemRect.size[1], 0);
|
||||||
|
m_hlVerts[1].m_pos.assign(0, y, 0);
|
||||||
|
m_hlVerts[2].m_pos.assign(itemRect.size[0], y+itemRect.size[1], 0);
|
||||||
|
m_hlVerts[3].m_pos.assign(itemRect.size[0], y, 0);
|
||||||
|
|
||||||
|
m_hlVertsBinding.load(m_hlVerts, sizeof(m_hlVerts));
|
||||||
|
}
|
||||||
|
|
||||||
void Menu::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
void Menu::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||||
{
|
{
|
||||||
|
m_scroll.mouseDown(coord, button, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ContentView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
void Menu::ContentView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||||
{
|
{
|
||||||
|
for (ViewChild<std::unique_ptr<ItemView>>& v : m_menu.m_items)
|
||||||
|
v.mouseDown(coord, button, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ItemView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
void Menu::ItemView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||||
|
@ -97,12 +157,13 @@ void Menu::ItemView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton
|
||||||
|
|
||||||
void Menu::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
void Menu::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||||
{
|
{
|
||||||
|
m_scroll.mouseUp(coord, button, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ContentView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
void Menu::ContentView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||||
{
|
{
|
||||||
|
for (ViewChild<std::unique_ptr<ItemView>>& v : m_menu.m_items)
|
||||||
|
v.mouseUp(coord, button, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ItemView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
void Menu::ItemView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
|
||||||
|
@ -112,57 +173,134 @@ void Menu::ItemView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton b
|
||||||
|
|
||||||
void Menu::mouseMove(const boo::SWindowCoord& coord)
|
void Menu::mouseMove(const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
|
m_scroll.mouseMove(coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ContentView::mouseMove(const boo::SWindowCoord& coord)
|
void Menu::ContentView::mouseMove(const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
|
for (ViewChild<std::unique_ptr<ItemView>>& v : m_menu.m_items)
|
||||||
|
v.mouseMove(coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ItemView::mouseEnter(const boo::SWindowCoord& coord)
|
void Menu::ItemView::mouseEnter(const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
|
m_menu.m_content->setHighlightedItem(m_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::mouseLeave(const boo::SWindowCoord& coord)
|
void Menu::mouseLeave(const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
|
m_scroll.mouseLeave(coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ContentView::mouseLeave(const boo::SWindowCoord& coord)
|
void Menu::ContentView::mouseLeave(const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
|
for (ViewChild<std::unique_ptr<ItemView>>& v : m_menu.m_items)
|
||||||
|
v.mouseLeave(coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ItemView::mouseLeave(const boo::SWindowCoord& coord)
|
void Menu::ItemView::mouseLeave(const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
|
m_menu.m_content->unsetHighlightedItem(m_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll)
|
void Menu::scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll)
|
||||||
{
|
{
|
||||||
|
m_scroll.scroll(coord, scroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::think()
|
||||||
|
{
|
||||||
|
m_scroll.m_view->think();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
void Menu::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
||||||
{
|
{
|
||||||
|
float pf = rootView().viewRes().pixelFactor();
|
||||||
boo::SWindowRect rect = sub;
|
boo::SWindowRect rect = sub;
|
||||||
|
rect.size[0] = m_cWidth;
|
||||||
|
if (rect.location[1] - m_cHeight < 0)
|
||||||
|
{
|
||||||
|
rect.location[1] += ROW_HEIGHT*pf;
|
||||||
|
rect.size[1] = std::min(root.size[1] - rect.location[1], m_cHeight);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rect.location[1] -= m_cHeight;
|
||||||
|
rect.size[1] = m_cHeight;
|
||||||
|
}
|
||||||
|
|
||||||
View::resized(root, rect);
|
View::resized(root, rect);
|
||||||
|
m_scroll.m_view->resized(root, rect);
|
||||||
|
setVerts(rect.size[0], rect.size[1]);
|
||||||
|
|
||||||
|
rect.location[0] += 5*pf;
|
||||||
|
rect.location[1] += rect.size[1] - (ROW_HEIGHT + ITEM_MARGIN - 5)*pf;
|
||||||
|
rect.size[0] = m_headText->nominalWidth();
|
||||||
|
rect.size[1] = m_headText->nominalHeight();
|
||||||
|
m_headText->resized(root, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ContentView::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
void Menu::ContentView::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub,
|
||||||
|
const boo::SWindowRect& scissor)
|
||||||
{
|
{
|
||||||
|
View::resized(root, sub);
|
||||||
|
float pf = rootView().viewRes().pixelFactor();
|
||||||
|
m_scissorRect = scissor;
|
||||||
|
boo::SWindowRect itemRect = sub;
|
||||||
|
itemRect.size[0] = m_menu.m_cWidth;
|
||||||
|
itemRect.size[1] = ROW_HEIGHT*pf;
|
||||||
|
itemRect.location[1] += sub.size[1] - m_menu.m_cTop + ITEM_MARGIN*pf;
|
||||||
|
int itemAdv = (ROW_HEIGHT + ITEM_MARGIN*2) * pf;
|
||||||
|
for (ViewChild<std::unique_ptr<ItemView>>& c : m_menu.m_items)
|
||||||
|
{
|
||||||
|
itemRect.location[1] -= itemAdv;
|
||||||
|
if (c.m_view)
|
||||||
|
c.m_view->resized(root, itemRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::ItemView::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
|
||||||
|
{
|
||||||
|
View::resized(root, sub);
|
||||||
|
float pf = rootView().viewRes().pixelFactor();
|
||||||
|
boo::SWindowRect textRect = sub;
|
||||||
|
textRect.location[0] += 5*pf;
|
||||||
|
textRect.location[1] += 5*pf;
|
||||||
|
m_textView->resized(root, textRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::draw(boo::IGraphicsCommandQueue* gfxQ)
|
void Menu::draw(boo::IGraphicsCommandQueue* gfxQ)
|
||||||
{
|
{
|
||||||
View::draw(gfxQ);
|
View::draw(gfxQ);
|
||||||
|
gfxQ->setShaderDataBinding(m_vertsBinding);
|
||||||
|
gfxQ->setDrawPrimitive(boo::Primitive::TriStrips);
|
||||||
|
gfxQ->draw(0, 4);
|
||||||
|
m_scroll.m_view->draw(gfxQ);
|
||||||
|
gfxQ->setShaderDataBinding(m_vertsBinding);
|
||||||
|
gfxQ->setDrawPrimitive(boo::Primitive::TriStrips);
|
||||||
|
gfxQ->draw(4, 4);
|
||||||
|
m_headText->draw(gfxQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ContentView::draw(boo::IGraphicsCommandQueue* gfxQ)
|
void Menu::ContentView::draw(boo::IGraphicsCommandQueue* gfxQ)
|
||||||
{
|
{
|
||||||
View::draw(gfxQ);
|
View::draw(gfxQ);
|
||||||
|
gfxQ->setScissor(m_scissorRect);
|
||||||
|
if (m_highlightedItem != -1)
|
||||||
|
{
|
||||||
|
gfxQ->setShaderDataBinding(m_hlVertsBinding);
|
||||||
|
gfxQ->draw(0, 4);
|
||||||
|
}
|
||||||
|
for (ViewChild<std::unique_ptr<ItemView>>& c : m_menu.m_items)
|
||||||
|
if (c.m_view)
|
||||||
|
c.m_view->draw(gfxQ);
|
||||||
|
gfxQ->setScissor(rootView().subRect());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::ItemView::draw(boo::IGraphicsCommandQueue* gfxQ)
|
||||||
|
{
|
||||||
|
View::draw(gfxQ);
|
||||||
|
m_textView->draw(gfxQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,14 @@ void RootView::resized(const boo::SWindowRect& root, const boo::SWindowRect&)
|
||||||
|
|
||||||
void RootView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods)
|
void RootView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods)
|
||||||
{
|
{
|
||||||
|
if (m_activeMenuButton)
|
||||||
|
{
|
||||||
|
ViewChild<std::unique_ptr<View>>& mv = m_activeMenuButton->getMenu();
|
||||||
|
if (!mv.mouseDown(coord, button, mods))
|
||||||
|
m_activeMenuButton->closeMenu(coord);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_hoverSplitDragView)
|
if (m_hoverSplitDragView)
|
||||||
{
|
{
|
||||||
m_activeSplitDragView = true;
|
m_activeSplitDragView = true;
|
||||||
|
@ -51,6 +59,13 @@ void RootView::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton butto
|
||||||
|
|
||||||
void RootView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods)
|
void RootView::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods)
|
||||||
{
|
{
|
||||||
|
if (m_activeMenuButton)
|
||||||
|
{
|
||||||
|
ViewChild<std::unique_ptr<View>>& mv = m_activeMenuButton->getMenu();
|
||||||
|
mv.mouseUp(coord, button, mods);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_activeSplitDragView && button == boo::EMouseButton::Primary)
|
if (m_activeSplitDragView && button == boo::EMouseButton::Primary)
|
||||||
{
|
{
|
||||||
m_activeSplitDragView = false;
|
m_activeSplitDragView = false;
|
||||||
|
@ -84,6 +99,13 @@ SplitView* RootView::recursiveTestSplitHover(SplitView* sv, const boo::SWindowCo
|
||||||
|
|
||||||
void RootView::mouseMove(const boo::SWindowCoord& coord)
|
void RootView::mouseMove(const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
|
if (m_activeMenuButton)
|
||||||
|
{
|
||||||
|
ViewChild<std::unique_ptr<View>>& mv = m_activeMenuButton->getMenu();
|
||||||
|
mv.mouseMove(coord);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_activeSplitDragView)
|
if (m_activeSplitDragView)
|
||||||
{
|
{
|
||||||
m_hoverSplitDragView->moveDragSplit(coord);
|
m_hoverSplitDragView->moveDragSplit(coord);
|
||||||
|
@ -150,12 +172,26 @@ void RootView::mouseEnter(const boo::SWindowCoord& coord)
|
||||||
|
|
||||||
void RootView::mouseLeave(const boo::SWindowCoord& coord)
|
void RootView::mouseLeave(const boo::SWindowCoord& coord)
|
||||||
{
|
{
|
||||||
|
if (m_activeMenuButton)
|
||||||
|
{
|
||||||
|
ViewChild<std::unique_ptr<View>>& mv = m_activeMenuButton->getMenu();
|
||||||
|
mv.mouseLeave(coord);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (View* v : m_views)
|
for (View* v : m_views)
|
||||||
v->mouseLeave(coord);
|
v->mouseLeave(coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootView::scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll)
|
void RootView::scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll)
|
||||||
{
|
{
|
||||||
|
if (m_activeMenuButton)
|
||||||
|
{
|
||||||
|
ViewChild<std::unique_ptr<View>>& mv = m_activeMenuButton->getMenu();
|
||||||
|
mv.scroll(coord, scroll);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (View* v : m_views)
|
for (View* v : m_views)
|
||||||
v->scroll(coord, scroll);
|
v->scroll(coord, scroll);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue