1
General / Re: Overlapping windows/menus
« on: November 13, 2015, 11:52:08 am »
Thanks for all the input, I had a busy week last week, but today I found some time to implent it. I did it as follows, if you ahve any tips/advice on my code, please share Tho it is not done yet and there is some debug stuff in there aswell, the colors that are filled from id and stuff. Later I will try to use a nice texture for the borders/titlebar aswell.
The basic idea is, every ui is on a panel. The panels are in a ui_manager, which handles all the events.
ui_manager, so the game_state has nothing to do with ui stuff, it just has a instance of the manager:
// .hpp file
namespace ui
{
class ui_manager
{
public:
ui_manager(sf::RenderWindow& window);
void clear();
void add_panel(std::shared_ptr<panel> panel);
std::shared_ptr<panel> get_panel(dword id);
bool handle_event(sf::Event::EventType type, sf::Vector2f mouse_pos);
void on_update();
void on_draw();
private:
sf::RenderWindow& m_window;
std::vector<std::shared_ptr<panel>> m_panels;
std::shared_ptr<panel> m_active_panel;
std::shared_ptr<panel> m_hovered_panel;
bool m_mouse_down = false;
};
}
//cpp file
#include "ui_manager.hpp"
namespace ui
{
ui_manager::ui_manager(sf::RenderWindow& window) :
m_window(window)
{
}
void ui_manager::clear()
{
m_panels.clear();
}
void ui_manager::add_panel(std::shared_ptr<panel> panel)
{
m_panels.push_back(panel);
}
bool ui_manager::handle_event(sf::Event::EventType type, sf::Vector2f mouse_pos)
{
if (type == sf::Event::MouseButtonPressed)
{
bool left = sf::Mouse::isButtonPressed(sf::Mouse::Button::Left);
m_active_panel.reset();
for (auto i = m_panels.begin(); i != m_panels.end(); )
{
if ((*i)->is_visible())
{
if ((*i)->getGlobalBounds().contains(mouse_pos))
{
(*i)->on_mouse_pressed(left, mouse_pos);
/*if (m_active_panel)
{
if (m_active_panel != *i)
{
m_active_panel = *i;
}
}
else
{
m_panels.erase(i);
m_panels.insert(m_panels.begin(), *i);
m_active_panel = *i;
}*/
m_mouse_down = true;
std::shared_ptr<panel> p = *i;
i = m_panels.erase(i);
m_panels.insert(m_panels.begin(), p);
m_active_panel = p;
std::cout << "current active panel: " << p->get_id() << std::endl;
return true;
}
}
++i;
}
}
else if (type == sf::Event::MouseButtonReleased)
{
m_mouse_down = false;
if (m_active_panel && m_active_panel->getGlobalBounds().contains(mouse_pos))
{
m_active_panel->on_mouse_released(mouse_pos);
return true;
}
}
else if (type == sf::Event::MouseMoved)
{
if (m_mouse_down)
{
m_active_panel->on_mouse_move(mouse_pos);
return true;
}
for (auto i = m_panels.begin(); i != m_panels.end(); )
{
if ((*i)->is_visible())
{
if ((*i)->getGlobalBounds().contains(mouse_pos))
{
(*i)->on_mouse_move(mouse_pos);
/*if (m_hovered_panel)
{
if (m_hovered_panel != *i)
{
m_hovered_panel->on_mouse_left();
}
}*/
m_hovered_panel = *i;
return true;
}
}
++i;
}
}
// more events, text input and shit
return false;
}
void ui_manager::on_update()
{
for (auto &i : m_panels)
{
i->on_update();
}
}
void ui_manager::on_draw()
{
for (auto i = m_panels.rbegin(); i != m_panels.rend(); ++i)
{
if ((*i)->is_visible())
{
m_window.draw(**i);
}
}
}
}
This is the panel class, every ui is basically on a panel
namespace ui
{
class panel : public sf::Drawable
{
public:
panel(dword id, sf::FloatRect rect);
explicit panel(dword id);
void add_widget(dword id, std::shared_ptr<widget> wg);
bool get_widget(dword id, std::shared_ptr<widget>& wg);
bool is_visible();
void on_update();
void on_key(char c);
void on_mouse_pressed(bool left, sf::Vector2f v);
void on_mouse_released(sf::Vector2f v);
void on_mouse_move(sf::Vector2f v);
void on_mouse_left();
void set_pos(sf::Vector2f v);
void set_size(float width, float height);
void set_visible(bool visible);
sf::FloatRect getGlobalBounds();
dword get_id();
private:
dword m_id = 0;
bool m_visible = false;
sf::RectangleShape m_background;
sf::RectangleShape m_titlebar;
sf::Vector2f m_pressed;
bool m_title_bar_pressed = false;
std::unordered_map<dword, std::shared_ptr<widget>> m_widgets;
dword m_widget_with_focus_id = 0;
dword m_widget_hovered_id = 0;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
};
}
//.cpp file
#include "panel.hpp"
#include <iostream>
namespace ui
{
panel::panel(dword id, sf::FloatRect rect)
{
m_id = id;
m_background.setSize(sf::Vector2f(rect.width, rect.height));
m_background.setPosition(rect.left, rect.top);
}
panel::panel(dword id)
{
m_id = id;
if (m_id == 1)
m_background.setFillColor(sf::Color::Green);
if (m_id == 2)
m_background.setFillColor(sf::Color::Red);
if (m_id == 3)
m_background.setFillColor(sf::Color(211, 211, 211));
m_background.setOutlineThickness(3);
m_background.setOutlineColor(sf::Color::Black);
m_titlebar.setFillColor(sf::Color::Blue);
}
void panel::add_widget(dword id, std::shared_ptr<widget> wg)
{
}
bool panel::get_widget(dword id, std::shared_ptr<widget>& wg)
{
auto it = m_widgets.find(id);
if (it == m_widgets.end())
return false;
wg = it->second;
return true;
}
bool panel::is_visible()
{
return m_visible;
}
void panel::on_update()
{
}
void panel::on_key(char c)
{
}
void panel::on_mouse_pressed(bool left, sf::Vector2f v)
{
if (m_titlebar.getGlobalBounds().contains(v))
{
m_title_bar_pressed = true;
m_pressed.x = v.x - m_background.getGlobalBounds().left;
m_pressed.y = v.y - m_background.getGlobalBounds().top;
}
}
void panel::on_mouse_released(sf::Vector2f v)
{
m_title_bar_pressed = false;
}
void panel::on_mouse_move(sf::Vector2f v)
{
if (m_title_bar_pressed)
{
m_background.setPosition(v.x - m_pressed.x, v.y - m_pressed.y);
m_titlebar.setPosition(v.x - m_pressed.x, v.y - m_pressed.y);
}
}
void panel::on_mouse_left()
{
}
void panel::set_visible(bool visible)
{
m_visible = visible;
}
void panel::set_pos(sf::Vector2f v)
{
m_background.setPosition(v);
m_titlebar.setPosition(v);
}
void panel::set_size(float width, float height)
{
m_background.setSize(sf::Vector2f(width, height));
m_titlebar.setSize(sf::Vector2f(width, 15));
}
sf::FloatRect panel::getGlobalBounds()
{
return m_background.getGlobalBounds();
}
dword panel::get_id()
{
return m_id;
}
void panel::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
target.draw(m_background);
target.draw(m_titlebar);
}
}
And this is how I call/init it, not this is just a copy paste of usefull code
Note that the classes are not yet done, I acn think of some optimaztions and stuff, but the general idae is there.
The basic idea is, every ui is on a panel. The panels are in a ui_manager, which handles all the events.
ui_manager, so the game_state has nothing to do with ui stuff, it just has a instance of the manager:
(click to show/hide)
// .hpp file
namespace ui
{
class ui_manager
{
public:
ui_manager(sf::RenderWindow& window);
void clear();
void add_panel(std::shared_ptr<panel> panel);
std::shared_ptr<panel> get_panel(dword id);
bool handle_event(sf::Event::EventType type, sf::Vector2f mouse_pos);
void on_update();
void on_draw();
private:
sf::RenderWindow& m_window;
std::vector<std::shared_ptr<panel>> m_panels;
std::shared_ptr<panel> m_active_panel;
std::shared_ptr<panel> m_hovered_panel;
bool m_mouse_down = false;
};
}
//cpp file
#include "ui_manager.hpp"
namespace ui
{
ui_manager::ui_manager(sf::RenderWindow& window) :
m_window(window)
{
}
void ui_manager::clear()
{
m_panels.clear();
}
void ui_manager::add_panel(std::shared_ptr<panel> panel)
{
m_panels.push_back(panel);
}
bool ui_manager::handle_event(sf::Event::EventType type, sf::Vector2f mouse_pos)
{
if (type == sf::Event::MouseButtonPressed)
{
bool left = sf::Mouse::isButtonPressed(sf::Mouse::Button::Left);
m_active_panel.reset();
for (auto i = m_panels.begin(); i != m_panels.end(); )
{
if ((*i)->is_visible())
{
if ((*i)->getGlobalBounds().contains(mouse_pos))
{
(*i)->on_mouse_pressed(left, mouse_pos);
/*if (m_active_panel)
{
if (m_active_panel != *i)
{
m_active_panel = *i;
}
}
else
{
m_panels.erase(i);
m_panels.insert(m_panels.begin(), *i);
m_active_panel = *i;
}*/
m_mouse_down = true;
std::shared_ptr<panel> p = *i;
i = m_panels.erase(i);
m_panels.insert(m_panels.begin(), p);
m_active_panel = p;
std::cout << "current active panel: " << p->get_id() << std::endl;
return true;
}
}
++i;
}
}
else if (type == sf::Event::MouseButtonReleased)
{
m_mouse_down = false;
if (m_active_panel && m_active_panel->getGlobalBounds().contains(mouse_pos))
{
m_active_panel->on_mouse_released(mouse_pos);
return true;
}
}
else if (type == sf::Event::MouseMoved)
{
if (m_mouse_down)
{
m_active_panel->on_mouse_move(mouse_pos);
return true;
}
for (auto i = m_panels.begin(); i != m_panels.end(); )
{
if ((*i)->is_visible())
{
if ((*i)->getGlobalBounds().contains(mouse_pos))
{
(*i)->on_mouse_move(mouse_pos);
/*if (m_hovered_panel)
{
if (m_hovered_panel != *i)
{
m_hovered_panel->on_mouse_left();
}
}*/
m_hovered_panel = *i;
return true;
}
}
++i;
}
}
// more events, text input and shit
return false;
}
void ui_manager::on_update()
{
for (auto &i : m_panels)
{
i->on_update();
}
}
void ui_manager::on_draw()
{
for (auto i = m_panels.rbegin(); i != m_panels.rend(); ++i)
{
if ((*i)->is_visible())
{
m_window.draw(**i);
}
}
}
}
This is the panel class, every ui is basically on a panel
(click to show/hide)
namespace ui
{
class panel : public sf::Drawable
{
public:
panel(dword id, sf::FloatRect rect);
explicit panel(dword id);
void add_widget(dword id, std::shared_ptr<widget> wg);
bool get_widget(dword id, std::shared_ptr<widget>& wg);
bool is_visible();
void on_update();
void on_key(char c);
void on_mouse_pressed(bool left, sf::Vector2f v);
void on_mouse_released(sf::Vector2f v);
void on_mouse_move(sf::Vector2f v);
void on_mouse_left();
void set_pos(sf::Vector2f v);
void set_size(float width, float height);
void set_visible(bool visible);
sf::FloatRect getGlobalBounds();
dword get_id();
private:
dword m_id = 0;
bool m_visible = false;
sf::RectangleShape m_background;
sf::RectangleShape m_titlebar;
sf::Vector2f m_pressed;
bool m_title_bar_pressed = false;
std::unordered_map<dword, std::shared_ptr<widget>> m_widgets;
dword m_widget_with_focus_id = 0;
dword m_widget_hovered_id = 0;
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
};
}
//.cpp file
#include "panel.hpp"
#include <iostream>
namespace ui
{
panel::panel(dword id, sf::FloatRect rect)
{
m_id = id;
m_background.setSize(sf::Vector2f(rect.width, rect.height));
m_background.setPosition(rect.left, rect.top);
}
panel::panel(dword id)
{
m_id = id;
if (m_id == 1)
m_background.setFillColor(sf::Color::Green);
if (m_id == 2)
m_background.setFillColor(sf::Color::Red);
if (m_id == 3)
m_background.setFillColor(sf::Color(211, 211, 211));
m_background.setOutlineThickness(3);
m_background.setOutlineColor(sf::Color::Black);
m_titlebar.setFillColor(sf::Color::Blue);
}
void panel::add_widget(dword id, std::shared_ptr<widget> wg)
{
}
bool panel::get_widget(dword id, std::shared_ptr<widget>& wg)
{
auto it = m_widgets.find(id);
if (it == m_widgets.end())
return false;
wg = it->second;
return true;
}
bool panel::is_visible()
{
return m_visible;
}
void panel::on_update()
{
}
void panel::on_key(char c)
{
}
void panel::on_mouse_pressed(bool left, sf::Vector2f v)
{
if (m_titlebar.getGlobalBounds().contains(v))
{
m_title_bar_pressed = true;
m_pressed.x = v.x - m_background.getGlobalBounds().left;
m_pressed.y = v.y - m_background.getGlobalBounds().top;
}
}
void panel::on_mouse_released(sf::Vector2f v)
{
m_title_bar_pressed = false;
}
void panel::on_mouse_move(sf::Vector2f v)
{
if (m_title_bar_pressed)
{
m_background.setPosition(v.x - m_pressed.x, v.y - m_pressed.y);
m_titlebar.setPosition(v.x - m_pressed.x, v.y - m_pressed.y);
}
}
void panel::on_mouse_left()
{
}
void panel::set_visible(bool visible)
{
m_visible = visible;
}
void panel::set_pos(sf::Vector2f v)
{
m_background.setPosition(v);
m_titlebar.setPosition(v);
}
void panel::set_size(float width, float height)
{
m_background.setSize(sf::Vector2f(width, height));
m_titlebar.setSize(sf::Vector2f(width, 15));
}
sf::FloatRect panel::getGlobalBounds()
{
return m_background.getGlobalBounds();
}
dword panel::get_id()
{
return m_id;
}
void panel::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
target.draw(m_background);
target.draw(m_titlebar);
}
}
And this is how I call/init it, not this is just a copy paste of usefull code
(click to show/hide)
std::shared_ptr<ui::panel> p(new ui::panel(1));
p->set_pos(sf::Vector2f(50, 50));
p->set_size(150, 90);
p->set_visible(true);
std::shared_ptr<ui::panel> s(new ui::panel(2));
s->set_pos(sf::Vector2f(100, 50));
s->set_size(250, 150);
s->set_visible(true);
std::shared_ptr<ui::panel> r(new ui::panel(3));
r->set_pos(sf::Vector2f(100, 110));
r->set_size(350, 150);
r->set_visible(true);
m_ui_manager.add_panel(p);
m_ui_manager.add_panel(s);
m_ui_manager.add_panel(r);
while (m_window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
m_window.close();
}
else
m_ui_manager.handle_event(event.type, static_cast<sf::Vector2f>(sf::Mouse::getPosition(m_window)));
}
m_ui_manager.on_update();
p->set_pos(sf::Vector2f(50, 50));
p->set_size(150, 90);
p->set_visible(true);
std::shared_ptr<ui::panel> s(new ui::panel(2));
s->set_pos(sf::Vector2f(100, 50));
s->set_size(250, 150);
s->set_visible(true);
std::shared_ptr<ui::panel> r(new ui::panel(3));
r->set_pos(sf::Vector2f(100, 110));
r->set_size(350, 150);
r->set_visible(true);
m_ui_manager.add_panel(p);
m_ui_manager.add_panel(s);
m_ui_manager.add_panel(r);
while (m_window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
m_window.close();
}
else
m_ui_manager.handle_event(event.type, static_cast<sf::Vector2f>(sf::Mouse::getPosition(m_window)));
}
m_ui_manager.on_update();
Note that the classes are not yet done, I acn think of some optimaztions and stuff, but the general idae is there.