This repository has been archived on 2023-05-13. You can view files and clone it, but cannot push or open issues or pull requests.
mipt_cpp/seminar11_events/01_select_move_delete/context_menu.hpp

143 lines
4.9 KiB
C++
Raw Normal View History

2023-01-03 13:35:56 +03:00
#pragma once
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
/*
Класс ContextMenu - контекстное меню
При нажатии правой кнопки мыши на экране появляется контекстное меню
Публичные методы:
ContextMenu(sf::RenderWindow&, const sf::Font&)
Конструктор принимает окно для отрисовки и шрифт
void addButton(const sf::String& name)
Добавить новый элемент в контекстное меню по имени name
void draw()
Нарисовать контекстное меню в окне, которое было передано в конструктор
int handleEvent(const sf::Event& event)
Обрабатывает событие event и возвращает целое число
Если это событие MousePressed и был выбран один из вариантов
контекстного меню, то вернёт номер этого варианта
Нумерация начинается с нуля
В ином случае вернёт -1
*/
class ContextMenu
{
private:
inline static const sf::Color kDefaultColor {sf::Color(190, 210, 190)};
inline static const sf::Color kHoverColor {sf::Color(150, 170, 150)};
inline static const sf::Color kTextColor {sf::Color::Black};
inline static const int kButtonHeight = 20;
inline static const int kCharacterSize = 16;
inline static const float kMenuWidthMultiplier = 1.2;
sf::RenderWindow& mRenderWindow;
sf::RectangleShape mShape;
sf::RectangleShape mHoverShape;
sf::Text mText;
std::vector<sf::String> mButtons;
bool mIsOpened = false;
bool mIsUpdated = false;
int mHoverPosition = -1;
int onMousePressed(const sf::Event& event)
{
if (event.mouseButton.button == sf::Mouse::Right) {
mIsOpened = true;
sf::Vector2f mousePosition = mRenderWindow.mapPixelToCoords({event.mouseButton.x, event.mouseButton.y});
mShape.setPosition(mousePosition);
}
if (event.mouseButton.button == sf::Mouse::Left && mIsOpened) {
mIsOpened = false;
return mHoverPosition;
}
return -1;
}
void onMouseMove(const sf::Event& event)
{
if (!mIsOpened) {
return;
}
sf::Vector2f mousePosition = mRenderWindow.mapPixelToCoords({event.mouseMove.x, event.mouseMove.y});
if (mShape.getGlobalBounds().contains(mousePosition)) {
mHoverPosition = (mousePosition.y - mShape.getPosition().y) / kButtonHeight;
}
else {
mHoverPosition = -1;
}
}
public:
ContextMenu(sf::RenderWindow& window, const sf::Font& font) : mRenderWindow(window)
{
mText.setFont(font);
mText.setCharacterSize(kCharacterSize);
mText.setFillColor(kTextColor);
mShape.setFillColor(kDefaultColor);
mHoverShape.setFillColor(kHoverColor);
mIsOpened = false;
mIsUpdated = false;
mHoverPosition = -1;
}
void addButton(const sf::String& name)
{
mButtons.push_back(name);
mIsUpdated = false;
}
void draw()
{
if (!mIsOpened) {
return;
}
// Если добавили новый вариант, то её текст может быть длиннее
// чем у других. Нужно расширить прямоугольники.
if (!mIsUpdated) {
int maxSizeX = 0;
for (int i = 0; i < mButtons.size(); i++) {
mText.setString(mButtons[i]);
if (mText.getLocalBounds().width > maxSizeX) {
maxSizeX = mText.getLocalBounds().width;
}
}
maxSizeX *= kMenuWidthMultiplier;
mShape.setSize({(float)maxSizeX, (float)(kButtonHeight * mButtons.size())});
mHoverShape.setSize({(float)maxSizeX, (float)(kButtonHeight)});
mIsUpdated = true;
}
mRenderWindow.draw(mShape);
if (mHoverPosition >= 0){
mHoverShape.setPosition(mShape.getPosition().x, mShape.getPosition().y + mHoverPosition * kButtonHeight);
mRenderWindow.draw(mHoverShape);
}
for (int i = 0; i < mButtons.size(); i++) {
mText.setString(mButtons[i]);
mText.setPosition(mShape.getPosition().x, mShape.getPosition().y + i * kButtonHeight);
mRenderWindow.draw(mText);
}
}
int handleEvent(const sf::Event& event) {
if (event.type == sf::Event::MouseMoved) {
onMouseMove(event);
}
else if (event.type == sf::Event::MouseButtonPressed) {
return onMousePressed(event);
}
return -1;
}
2023-01-04 00:36:53 +03:00
};