Implement test drop menu and wrap up menu bar system

This commit is contained in:
NaiJi ✨ 2021-08-26 19:54:30 +03:00
parent be0367bba0
commit 37391cfdfd
17 changed files with 213 additions and 48 deletions

View File

@ -27,7 +27,8 @@ private:
std::shared_ptr<Game> _game; std::shared_ptr<Game> _game;
void exec(); void exec();
void emplaceState(GUIState::Tag new_state); void pushState(GUIState::Tag new_state);
void popState();
}; };
#endif // APPLICATION_H #endif // APPLICATION_H

View File

@ -23,11 +23,12 @@ Application::Application() :
_game_window.setMouseCursorGrabbed(false); _game_window.setMouseCursorGrabbed(false);
_game_window.setVerticalSyncEnabled(true); _game_window.setVerticalSyncEnabled(true);
MainMenu::Callbacks callbacks = {[&](){ emplaceState(GUIState::Tag::EDITOR); }}; MainMenu::Callbacks callbacks = {[&](){ pushState(GUIState::Tag::EDITOR); }};
Editor::Callbacks editor_callbacks = {[&](){ popState(); }};
const auto main_menu = std::make_shared<MainMenu>(_game_window, std::move(callbacks)); const auto main_menu = std::make_shared<MainMenu>(_game_window, std::move(callbacks));
const auto game_state = std::make_shared<GameState>(_game_window, _game, GameState::Callbacks()); const auto game_state = std::make_shared<GameState>(_game_window, _game, GameState::Callbacks());
const auto editor = std::make_shared<Editor>(_game_window, Editor::Callbacks(), std::make_unique<MusicSFML>()); const auto editor = std::make_shared<Editor>(_game_window, std::move(editor_callbacks), std::make_unique<MusicSFML>());
_states[GUIState::Tag::MAIN_MENU] = main_menu; _states[GUIState::Tag::MAIN_MENU] = main_menu;
_states[GUIState::Tag::GAME] = game_state; _states[GUIState::Tag::GAME] = game_state;
_states[GUIState::Tag::EDITOR] = editor; _states[GUIState::Tag::EDITOR] = editor;
@ -95,9 +96,16 @@ void Application::draw()
_game_window.display(); _game_window.display();
} }
void Application::emplaceState(GUIState::Tag new_state) void Application::pushState(GUIState::Tag new_state)
{ {
_state_stack.back()->leave(); _state_stack.back()->leave();
_state_stack.emplace_back(_states.at(new_state)); _state_stack.emplace_back(_states.at(new_state));
_state_stack.back()->enter(); _state_stack.back()->enter();
} }
void Application::popState()
{
_state_stack.back()->leave();
_state_stack.pop_back();
_state_stack.back()->enter();
}

View File

@ -16,7 +16,26 @@ Editor::Editor(sf::RenderWindow& game_window, Callbacks&& callbacks, std::unique
const float window_width = game_window.getSize().x; const float window_width = game_window.getSize().x;
//const float window_height = game_window.getSize().y; //const float window_height = game_window.getSize().y;
auto bpm_button = std::make_shared<PushButton>("Calculate BPM");
bpm_button->setCallback([&]()
{
_bpm_calculator->startListening(0);
});
auto quit_button = std::make_shared<PushButton>("Quit");
quit_button->setCallback(callbacks.onLeaveEditorState);
auto test_menu = std::make_shared<MenuDrop>();
test_menu->setRect(sf::FloatRect{0, 0, 200, 27});
_menu_bar->setRect(sf::FloatRect(0, 0, window_width, 27)); _menu_bar->setRect(sf::FloatRect(0, 0, window_width, 27));
_menu_bar->addSubMenu("test", test_menu);
test_menu->addPushButton(bpm_button);
test_menu->addPushButton(quit_button);
_music->openFromFile("Uta-test.flac");
_music->setVolume(5);
} }
void Editor::input(const sf::Event& event) void Editor::input(const sf::Event& event)

View File

@ -14,7 +14,7 @@ public:
struct Callbacks struct Callbacks
{ {
std::function<void(void)> onAppendGameState; std::function<void(void)> onLeaveEditorState;
}; };
explicit Editor(sf::RenderWindow& game_window, Callbacks&& callbacks, std::unique_ptr<Music>&& music); explicit Editor(sf::RenderWindow& game_window, Callbacks&& callbacks, std::unique_ptr<Music>&& music);

View File

@ -1,5 +1,5 @@
#include "mainmenu.h" #include "mainmenu.h"
#include "widgets/button.h" #include "widgets/pushbutton.h"
#include "widgets/group.h" #include "widgets/group.h"
MainMenu::MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks) : MainMenu::MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks) :
@ -9,11 +9,11 @@ MainMenu::MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks) :
const float window_width = game_window.getSize().x; const float window_width = game_window.getSize().x;
const float window_height = game_window.getSize().y; const float window_height = game_window.getSize().y;
std::shared_ptr<Button> button_start = std::make_shared<Button>("Start"); std::shared_ptr<PushButton> button_start = std::make_shared<PushButton>("Start");
button_start->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 2, window_width / 3., window_height / 7.)); button_start->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 2, window_width / 3., window_height / 7.));
button_start->setCallback(callbacks.onAppendGameState); button_start->setCallback(callbacks.onAppendGameState);
std::shared_ptr<Button> button_exit = std::make_shared<Button>("Exit"); std::shared_ptr<PushButton> button_exit = std::make_shared<PushButton>("Exit");
button_exit->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 4, window_width / 3., window_height / 7.)); button_exit->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 4, window_width / 3., window_height / 7.));
button_exit->setCallback([&]() button_exit->setCallback([&]()
{ {

View File

@ -14,8 +14,11 @@ void Button::update()
void Button::draw(sf::RenderTarget& target, sf::RenderStates states) const void Button::draw(sf::RenderTarget& target, sf::RenderStates states) const
{ {
target.draw(_button_content, states); if (_is_visible)
target.draw(_button_text, states); {
target.draw(_button_content, states);
target.draw(_button_text, states);
}
Widget::draw(target, states); Widget::draw(target, states);
} }
@ -36,11 +39,6 @@ bool Button::isUnderMouse(int mouse_x, int mouse_y) const
return _is_visible && _button_content.getGlobalBounds().contains(mouse_x, mouse_y); return _is_visible && _button_content.getGlobalBounds().contains(mouse_x, mouse_y);
} }
void Button::setFillColor(sf::Color&& color)
{
_button_content.setFillColor(std::move(color));
}
void Button::setText(const std::string& text) void Button::setText(const std::string& text)
{ {
_button_text.setString(text); _button_text.setString(text);

View File

@ -13,11 +13,11 @@ public:
virtual void input(const sf::Event& event) override = 0; virtual void input(const sf::Event& event) override = 0;
virtual void update() override final; virtual void update() override final;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override final; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override final;
virtual void setRect(const sf::FloatRect& rect) override final;
virtual void setPosition(const sf::Vector2f& position) override final; virtual void setPosition(const sf::Vector2f& position) override final;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override final; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override final;
virtual void setFillColor(sf::Color&& color); virtual void setRect(const sf::FloatRect& rect) override;
virtual void setText(const std::string& text); virtual void setText(const std::string& text);
protected: protected:

View File

@ -7,7 +7,8 @@ CascadeMenuButton::CascadeMenuButton(const std::string& text) :
void CascadeMenuButton::input(const sf::Event& event) void CascadeMenuButton::input(const sf::Event& event)
{ {
Button::input(event); if (!_submenu)
return;
switch (event.type) switch (event.type)
{ {
@ -29,7 +30,28 @@ void CascadeMenuButton::input(const sf::Event& event)
} }
} }
void CascadeMenuButton::setRect(const sf::FloatRect& rect)
{
Button::setRect(rect);
resetRect(_submenu);
}
void CascadeMenuButton::setSubmenu(const std::shared_ptr<MenuDrop>& submenu) void CascadeMenuButton::setSubmenu(const std::shared_ptr<MenuDrop>& submenu)
{ {
_submenu = submenu; _submenu = submenu;
resetRect(_submenu);
}
const std::shared_ptr<MenuDrop> CascadeMenuButton::submenu() const
{
return _submenu;
}
void CascadeMenuButton::resetRect(const std::shared_ptr<MenuDrop>& submenu)
{
if (submenu)
{
submenu->setPosition({_button_content.getPosition().x + _button_content.getSize().x,
_button_content.getPosition().y});
}
} }

View File

@ -9,10 +9,14 @@ class CascadeMenuButton : public Button
public: public:
explicit CascadeMenuButton(const std::string& text); explicit CascadeMenuButton(const std::string& text);
virtual void input(const sf::Event& event) override final; virtual void input(const sf::Event& event) override final;
virtual void setRect(const sf::FloatRect& rect) override final;
void setSubmenu(const std::shared_ptr<MenuDrop>& submenu); void setSubmenu(const std::shared_ptr<MenuDrop>& submenu);
const std::shared_ptr<MenuDrop> submenu() const;
private: private:
std::shared_ptr<MenuDrop> _submenu; std::shared_ptr<MenuDrop> _submenu;
void resetRect(const std::shared_ptr<MenuDrop>& submenu);
}; };

View File

@ -9,6 +9,20 @@ MenuBar::MenuBar() :
void MenuBar::input(const sf::Event &event) void MenuBar::input(const sf::Event &event)
{ {
switch (event.type)
{
default:
break;
case sf::Event::MouseButtonReleased:
if (!isUnderMouse(event.mouseButton.x, event.mouseButton.y))
{
for (auto& submenu : _submenus)
submenu->unlock();
}
break;
}
Widget::input(event); Widget::input(event);
} }
@ -19,7 +33,9 @@ void MenuBar::update()
void MenuBar::draw(sf::RenderTarget& target, sf::RenderStates states) const void MenuBar::draw(sf::RenderTarget& target, sf::RenderStates states) const
{ {
target.draw(_bar_rect, states); if (_is_visible)
target.draw(_bar_rect, states);
Widget::draw(target, states); Widget::draw(target, states);
} }
@ -51,15 +67,32 @@ bool MenuBar::isUnderMouse(int mouse_x, int mouse_y) const
void MenuBar::addSubMenu(std::string name, const std::shared_ptr<MenuDrop>& submenu) void MenuBar::addSubMenu(std::string name, const std::shared_ptr<MenuDrop>& submenu)
{ {
const auto new_button = std::make_shared<Button>(name); const auto new_button = std::make_shared<PushButton>(name);
std::size_t current_index = _amount_buttons; std::size_t current_index = _amount_buttons;
new_button->setRect(sf::FloatRect(current_index * _button_width, 0, _button_width, _bar_rect.getSize().y)); new_button->setRect(sf::FloatRect(current_index * _button_width, 0, _button_width, _bar_rect.getSize().y));
new_button->setCallback([submenu=submenu]() new_button->setCallback([submenu=submenu]()
{ {
submenu->trigger(); submenu->setVisibility(true);
submenu->lock();
}); });
new_button->setFillColor(sf::Color(171, 141, 189)); submenu->setPosition({static_cast<float>(current_index * _button_width),
_bar_rect.getSize().y});
new_button->setFillColors(sf::Color(171, 141, 189), sf::Color(48, 27, 57));
addChild(new_button); addChild(new_button);
addChild(submenu);
_submenus.emplace_back(submenu);
++_amount_buttons;
}
void MenuBar::setVisibility(bool is_visible)
{
Widget::setVisibility(is_visible);
for (auto& submenu : _submenus)
submenu->setVisibility(false);
} }

View File

@ -17,6 +17,7 @@ public:
virtual void setRect(const sf::FloatRect& rect) override; virtual void setRect(const sf::FloatRect& rect) override;
virtual void setPosition(const sf::Vector2f& position) override; virtual void setPosition(const sf::Vector2f& position) override;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override;
virtual void setVisibility(bool is_visible = true) override;
void addSubMenu(std::string name, const std::shared_ptr<MenuDrop>& submenu); void addSubMenu(std::string name, const std::shared_ptr<MenuDrop>& submenu);
@ -24,4 +25,6 @@ private:
sf::RectangleShape _bar_rect; sf::RectangleShape _bar_rect;
std::size_t _amount_buttons; std::size_t _amount_buttons;
std::size_t _button_width; std::size_t _button_width;
std::vector<std::shared_ptr<MenuDrop>> _submenus;
}; };

View File

@ -5,32 +5,43 @@ MenuDrop::MenuDrop() :
_is_locked(false), _is_locked(false),
_button_height(27), _button_height(27),
_button_index(0) _button_index(0)
{} {
setVisibility(false);
_content_rect.setFillColor(sf::Color(200, 200, 200));
}
void MenuDrop::input(const sf::Event& event) void MenuDrop::input(const sf::Event& event)
{ {
if (!_is_visible) if (!_is_visible)
return; return;
Widget::input(event);
switch (event.type) switch (event.type)
{ {
default: default:
break; break;
case sf::Event::MouseButtonReleased:
case sf::Event::MouseMoved: case sf::Event::MouseMoved:
if (isUnderMouse(event.mouseButton.x, event.mouseButton.y)) if (!isUnderMouse(event.mouseButton.x, event.mouseButton.y))
{ {
Widget::input(event); if (!isLocked() && !hasActiveSubmenus())
}
else
{
if (!isLocked())
setVisibility(false); setVisibility(false);
} }
break; break;
} }
} }
bool MenuDrop::hasActiveSubmenus() const
{
return std::any_of(_submenus.begin(), _submenus.end(),
[](const auto& submenu) -> bool
{
return submenu->_is_visible;
});
}
void MenuDrop::update() void MenuDrop::update()
{ {
Widget::update(); Widget::update();
@ -38,7 +49,9 @@ void MenuDrop::update()
void MenuDrop::draw(sf::RenderTarget& target, sf::RenderStates states) const void MenuDrop::draw(sf::RenderTarget& target, sf::RenderStates states) const
{ {
target.draw(_content_rect); if (_is_visible)
target.draw(_content_rect);
Widget::draw(target, states); Widget::draw(target, states);
} }
@ -58,8 +71,24 @@ bool MenuDrop::isUnderMouse(int mouse_x, int mouse_y) const
return _is_visible && _content_rect.getGlobalBounds().contains(mouse_x, mouse_y); return _is_visible && _content_rect.getGlobalBounds().contains(mouse_x, mouse_y);
} }
void MenuDrop::addButton(const std::shared_ptr<Button>& button) void MenuDrop::setVisibility(bool is_visible)
{ {
Widget::setVisibility(is_visible);
if (!is_visible)
_is_locked = false;
}
void MenuDrop::addPushButton(const std::shared_ptr<PushButton>& button)
{
add(button);
}
void MenuDrop::addCascadeButton(const std::shared_ptr<CascadeMenuButton>& button)
{
auto& submenu = button->submenu();
submenu->setParent(_parent);
_submenus.emplace_back();
add(button); add(button);
} }
@ -79,6 +108,7 @@ void MenuDrop::add(const std::shared_ptr<Widget> &widget)
_button_height)); _button_height));
addChild(widget); addChild(widget);
++_button_index;
} }
void MenuDrop::lock() void MenuDrop::lock()

View File

@ -1,7 +1,8 @@
#pragma once #pragma once
#include "widget.h" #include "widget.h"
#include "button.h" #include "pushbutton.h"
#include "cascademenubutton.h"
#include <SFML/Graphics/RectangleShape.hpp> #include <SFML/Graphics/RectangleShape.hpp>
class MenuDrop : public Widget class MenuDrop : public Widget
@ -15,8 +16,10 @@ public:
virtual void setRect(const sf::FloatRect& rect) override; virtual void setRect(const sf::FloatRect& rect) override;
virtual void setPosition(const sf::Vector2f& position) override; virtual void setPosition(const sf::Vector2f& position) override;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override;
virtual void setVisibility(bool is_visible = true) override;
void addButton(const std::shared_ptr<Button>& button); void addPushButton(const std::shared_ptr<PushButton>& button);
void addCascadeButton(const std::shared_ptr<CascadeMenuButton>& button);
void addSeparator(); void addSeparator();
void lock(); void lock();
@ -30,5 +33,8 @@ private:
std::size_t _button_height; std::size_t _button_height;
std::size_t _button_index; std::size_t _button_index;
std::vector<std::shared_ptr<MenuDrop>> _submenus;
void add(const std::shared_ptr<Widget>& widget); void add(const std::shared_ptr<Widget>& widget);
bool hasActiveSubmenus() const;
}; };

View File

@ -3,12 +3,13 @@
PushButton::PushButton(const std::string& text) : PushButton::PushButton(const std::string& text) :
Button(text), Button(text),
_pressed(false) _pressed(false)
{} {
_color_idle = sf::Color(230, 230, 230);
_color_pressed = sf::Color(200, 200, 200);
}
void PushButton::input(const sf::Event& event) void PushButton::input(const sf::Event& event)
{ {
Button::input(event);
switch (event.type) switch (event.type)
{ {
default: default:
@ -18,14 +19,14 @@ void PushButton::input(const sf::Event& event)
if (isUnderMouse(event.mouseButton.x, event.mouseButton.y)) if (isUnderMouse(event.mouseButton.x, event.mouseButton.y))
{ {
_pressed = true; _pressed = true;
_button_content.setFillColor(sf::Color(155, 155, 155)); _button_content.setFillColor(sf::Color(_color_pressed));
} }
break; break;
case sf::Event::MouseButtonReleased: case sf::Event::MouseButtonReleased:
if (_pressed) if (_pressed)
{ {
_button_content.setFillColor(sf::Color::White); _button_content.setFillColor(_color_idle);
_pressed = false; _pressed = false;
if (isUnderMouse(event.mouseButton.x, event.mouseButton.y)) if (isUnderMouse(event.mouseButton.x, event.mouseButton.y))
_on_click_callback(); _on_click_callback();
@ -38,3 +39,11 @@ void PushButton::setCallback(std::function<void(void)> callback)
{ {
_on_click_callback = callback; _on_click_callback = callback;
} }
void PushButton::setFillColors(sf::Color&& idle_color, sf::Color&& pressed_color)
{
_color_idle = idle_color;
_color_pressed = pressed_color;
_button_content.setFillColor(_pressed ? pressed_color : idle_color);
}

View File

@ -9,8 +9,12 @@ public:
virtual void input(const sf::Event& event) override final; virtual void input(const sf::Event& event) override final;
void setCallback(std::function<void(void)> callback); void setCallback(std::function<void(void)> callback);
void setFillColors(sf::Color&& idle_color, sf::Color&& pressed_color);
private: private:
sf::Color _color_idle;
sf::Color _color_pressed;
bool _pressed; bool _pressed;
std::function<void(void)> _on_click_callback; std::function<void(void)> _on_click_callback;
}; };

View File

@ -2,8 +2,13 @@
void Widget::input(const sf::Event &event) void Widget::input(const sf::Event &event)
{ {
for (auto& child : _children) if (_blocker)
child->input(event); _blocker->input(event);
else
{
for (auto& child : _children)
child->input(event);
}
} }
void Widget::update() void Widget::update()
@ -14,9 +19,6 @@ void Widget::update()
void Widget::draw(sf::RenderTarget& target, sf::RenderStates states) const void Widget::draw(sf::RenderTarget& target, sf::RenderStates states) const
{ {
if (!_is_visible)
return;
for (auto& child : _children) for (auto& child : _children)
child->draw(target, states); child->draw(target, states);
} }
@ -25,11 +27,33 @@ void Widget::setVisibility(bool is_visible)
{ {
_is_visible = is_visible; _is_visible = is_visible;
for (auto& child : _children) for (auto& child : _children)
child->setVisibility(false); child->setVisibility(_is_visible);
} }
void Widget::addChild(const std::shared_ptr<Widget> &child) void Widget::addChild(const std::shared_ptr<Widget>& child)
{ {
child->_parent = shared_from_this(); child->setParent(shared_from_this());
_children.emplace_back(child);
const auto iterator = std::find(_children.begin(), _children.end(), child);
if (iterator == _children.end())
_children.emplace_back(child);
}
void Widget::setParent(const std::shared_ptr<Widget>& parent)
{
if (_parent != parent)
{
_parent = parent;
_parent->addChild(shared_from_this());
}
}
void Widget::blockBy(const std::shared_ptr<Widget>& blocker)
{
_blocker = blocker;
}
void Widget::unblock()
{
_blocker = nullptr;
} }

View File

@ -18,12 +18,16 @@ public:
virtual void setPosition(const sf::Vector2f& position) = 0; virtual void setPosition(const sf::Vector2f& position) = 0;
virtual bool isUnderMouse(int mouse_x, int mouse_y) const = 0; virtual bool isUnderMouse(int mouse_x, int mouse_y) const = 0;
void setVisibility(bool is_visible = true); virtual void setVisibility(bool is_visible = true);
void addChild(const std::shared_ptr<Widget>& child); void addChild(const std::shared_ptr<Widget>& child);
void setParent(const std::shared_ptr<Widget>& parent);
void blockBy(const std::shared_ptr<Widget>& blocker);
void unblock();
protected: protected:
std::vector<std::shared_ptr<Widget>> _children; std::vector<std::shared_ptr<Widget>> _children;
std::shared_ptr<Widget> _parent; std::shared_ptr<Widget> _parent;
std::shared_ptr<Widget> _blocker;
bool _is_visible = true; bool _is_visible = true;
}; };