#include "pie-menu.hpp" #include 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(EaseFunction::OUT_SINE, 0.0f, 0.1f, 0.0f, 1.0f); scaleUpTween->setUpdateCallback(std::bind(&PieMenu::setScale, this, std::placeholders::_1)); scaleDownTween = new Tween(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(i + 1) / static_cast(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 selectedCallback, std::function 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(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(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); } }