💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

218 lines
6.0 KiB

#include "pie-menu.hpp"
#include <cmath>
PieMenu::PieMenu(Tweener* tweener):
tweener(tweener),
scaleUpTween(nullptr),
scaleDownTween(nullptr),
scale(1.0f),
selectionIndex(0),
dragging(false),
dragStart(0.0f)
{
// Setup fullscreen container
fullscreenContainer.addChild(&croppedContainer);
fullscreenContainer.setMouseMovedCallback(std::bind(&PieMenu::mouseMoved, this, std::placeholders::_1, std::placeholders::_2));
fullscreenContainer.setMousePressedCallback(std::bind(&PieMenu::mouseButtonPressed, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
fullscreenContainer.setMouseReleasedCallback(std::bind(&PieMenu::mouseButtonReleased, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
// Setup cropped container
croppedContainer.addChild(&scalingContainer);
// Setup scaling container
scalingContainer.setAnchor(Vector2(0.5f));
// Create tweens
scaleUpTween = new Tween<float>(EaseFunction::OUT_SINE, 0.0f, 0.1f, 0.0f, 1.0f);
scaleUpTween->setUpdateCallback(std::bind(&PieMenu::setScale, this, std::placeholders::_1));
scaleDownTween = new Tween<float>(EaseFunction::IN_SINE, 0.0f, 0.1f, 1.0f, -1.0f);
scaleDownTween->setUpdateCallback(std::bind(&PieMenu::setScale, this, std::placeholders::_1));
// Add tweens
tweener->addTween(scaleUpTween);
tweener->addTween(scaleDownTween);
}
void PieMenu::resize()
{
float iconDistance = 0.0f;
if (fullscreenContainer.getParent() == nullptr)
{
return;
}
// Resize fullscreen container
fullscreenContainer.setDimensions(fullscreenContainer.getParent()->getDimensions());
// Resize cropped container
croppedContainer.setDimensions(Vector2(options[0]->getTexture()->getWidth(), options[0]->getTexture()->getHeight()));
// Place options
for (std::size_t i = 0; i < options.size(); ++i)
{
float angle = glm::radians(360.0f) / static_cast<float>(i + 1) / static_cast<float>(options.size());
options[i]->setAnchor(Vector2(0.5f, 0.5f));
options[i]->setTranslation(Vector2(0.0f, 0.0f));
icons[i]->setAnchor(Vector2(0.5f, 0.5f));
icons[i]->setTranslation(Vector2(0.0f, 0.0f));
}
}
void PieMenu::setScale(float scale)
{
for (std::size_t i = 0; i < options.size(); ++i)
{
options[i]->setDimensions(Vector2(options[i]->getTexture()->getWidth(), options[i]->getTexture()->getHeight()) * scale);
icons[i]->setDimensions(Vector2(icons[i]->getTexture()->getWidth(), icons[i]->getTexture()->getHeight()) * scale);
}
}
void PieMenu::addOption(Texture* backgroundTexture, Texture* iconTexture, std::function<void()> selectedCallback, std::function<void()> deselectedCallback)
{
// Allocate new option
UIImage* option = new UIImage();
option->setTexture(backgroundTexture);
option->setDimensions(Vector2(backgroundTexture->getWidth(), backgroundTexture->getHeight()));
option->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.50f));
options.push_back(option);
UIImage* icon = new UIImage();
icon->setTexture(iconTexture);
icon->setDimensions(Vector2(iconTexture->getWidth(), iconTexture->getHeight()));
icon->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.50f));
icons.push_back(icon);
// Add icon to option
scalingContainer.addChild(icon);
// Add option to menu
scalingContainer.addChild(option);
// Setup callbacks
selectedCallbacks.push_back(selectedCallback);
deselectedCallbacks.push_back(deselectedCallback);
}
void PieMenu::select(std::size_t index)
{
if (index != selectionIndex && selectionIndex < options.size())
{
deselect(selectionIndex);
}
selectionIndex = index;
selectedCallbacks[index]();
}
void PieMenu::deselect(std::size_t index)
{
deselectedCallbacks[index]();
}
void PieMenu::highlight(std::size_t index)
{
options[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
icons[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f));
}
void PieMenu::unhighlight(std::size_t index)
{
options[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.50f));
icons[index]->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.50f));
}
void PieMenu::mouseMoved(int x, int y)
{
if (dragging)
{
Vector2 direction = Vector2(x, y) - dragStart;
if (direction.x != 0.0f || direction.y != 0.0f)
{
direction = glm::normalize(direction);
// Calculate arc length
float arcLength = glm::radians(360.0f) / static_cast<float>(options.size());
// Calculate angle between cursor and pie menu
float angle = std::atan2(direction.y, direction.x) + glm::radians(90.0f) + arcLength * 0.5f;
while (angle < 0.0f) angle += glm::radians(360.0f);
// Determine option index from angle
std::size_t index = static_cast<std::size_t>(angle / arcLength);
if (index != highlightedIndex)
{
if (highlightedIndex < options.size())
{
unhighlight(highlightedIndex);
}
highlight(index);
highlightedIndex = index;
}
}
}
}
void PieMenu::mouseButtonPressed(int button, int x, int y)
{
if (button == 3)
{
// Start dragging
dragging = true;
dragStart.x = x;
dragStart.y = y;
// Set pie menu position
Vector2 halfDimensions = croppedContainer.getDimensions() * 0.5f;
croppedContainer.setTranslation(Vector2(x - halfDimensions.x, y - halfDimensions.y));
// Clear highlights
for (std::size_t i = 0; i < options.size(); ++i)
{
unhighlight(i);
}
// Reset highlighted index
highlightedIndex = options.size();
// Show pie menu
fullscreenContainer.setVisible(true);
// Start scale-up tween
scaleDownTween->stop();
scaleUpTween->start();
}
}
void PieMenu::mouseButtonReleased(int button, int x, int y)
{
if (button == 3)
{
// Stop dragging
dragging = false;
// Select highlighted index
if (highlightedIndex != selectionIndex && highlightedIndex < options.size())
{
select(highlightedIndex);
}
// Clear highlights
for (std::size_t i = 0; i < options.size(); ++i)
{
unhighlight(i);
}
// Reset highlighted index
highlightedIndex = options.size();
// Start scale-down tween
scaleUpTween->stop();
scaleDownTween->start();
//fullscreenContainer.setVisible(false);
}
}