This repository has been archived on 2025-03-20. You can view files and clone it, but cannot push or open issues or pull requests.
The-Powder-Toy/src/gui/interface/DirectionSelector.cpp
2023-04-30 14:22:56 +02:00

168 lines
4.3 KiB
C++

#include "DirectionSelector.h"
namespace ui {
DirectionSelector::DirectionSelector(ui::Point position, float scale, int radius, int handleRadius, int snapPointRadius, int snapPointEffectRadius):
ui::Component(position, ui::Point(radius * 5 / 2, radius * 5 / 2)),
scale(scale),
radius(radius),
handleRadius(handleRadius),
useSnapPoints(false),
snapPointRadius(snapPointRadius),
snapPointEffectRadius(snapPointEffectRadius),
autoReturn(false),
backgroundColor(ui::Colour(0, 0, 0, 63)),
foregroundColor(ui::Colour(63, 63, 63, 127)),
borderColor(ui::Colour(255, 255, 255)),
snapPointColor(ui::Colour(63, 63, 63, 127)),
updateCallback(nullptr),
changeCallback(nullptr),
mouseDown(false),
mouseHover(false),
altDown(false),
value({ { 0, 0 }, 0, 0 })
{
}
void DirectionSelector::CheckHovering(int x, int y)
{
mouseHover = std::hypot((value.offset.X + radius) - x, (value.offset.Y + radius) - y) < handleRadius;
}
DirectionSelector::Value DirectionSelector::GravityValueToValue(float x, float y)
{
return { { int(x / scale), int(y / scale) }, x, y };
}
DirectionSelector::Value DirectionSelector::PositionToValue(ui::Point position)
{
auto length = std::hypot(float(position.X), float(position.Y));
if (length > radius)
{
position.X = int(position.X / length * radius);
position.Y = int(position.Y / length * radius);
}
return { position, position.X * scale, position.Y * scale };
}
void DirectionSelector::SetSnapPoints(int newSnapPointEffectRadius, int points, float maxMagnitude)
{
snapPointEffectRadius = newSnapPointEffectRadius;
snapPoints.clear();
snapPoints.push_back(GravityValueToValue(0, 0));
for (int i = 1; i < points; i++)
{
auto dist = i / float(points - 1) * maxMagnitude;
snapPoints.push_back(GravityValueToValue( dist, 0));
snapPoints.push_back(GravityValueToValue( 0, dist));
snapPoints.push_back(GravityValueToValue(-dist, 0));
snapPoints.push_back(GravityValueToValue( 0, -dist));
}
useSnapPoints = true;
}
void DirectionSelector::ClearSnapPoints()
{
useSnapPoints = false;
snapPoints.clear();
}
float DirectionSelector::GetXValue()
{
return value.xValue;
}
float DirectionSelector::GetYValue()
{
return value.yValue;
}
void DirectionSelector::SetPositionAbs(ui::Point position)
{
SetPosition(position - ui::Point{ radius + handleRadius, radius + handleRadius });
}
void DirectionSelector::SetPosition(ui::Point position)
{
value = PositionToValue(position);
if (useSnapPoints && !altDown)
{
for (auto &point : snapPoints)
{
if (std::hypot(point.offset.X - position.X, point.offset.Y - position.Y) <= snapPointEffectRadius)
{
value = point;
}
}
}
if (updateCallback)
{
updateCallback(GetXValue(), GetYValue());
}
}
void DirectionSelector::SetValues(float x, float y)
{
value.xValue = x;
value.yValue = y;
SetPosition(GravityValueToValue(x, y).offset);
}
void DirectionSelector::Draw(const ui::Point& screenPos)
{
Graphics * g = GetGraphics();
auto handleTrackRadius = radius + handleRadius;
ui::Point center = screenPos + Vec2{ handleTrackRadius, handleTrackRadius };
g->BlendFilledEllipse(center, { handleTrackRadius, handleTrackRadius }, backgroundColor);
g->BlendEllipse(center, { handleTrackRadius, handleTrackRadius }, borderColor);
for (auto &point : snapPoints)
{
g->BlendFilledRect(
RectBetween(
center + point.offset - Vec2{ snapPointRadius, snapPointRadius },
center + point.offset + Vec2{ snapPointRadius, snapPointRadius }
),
snapPointColor.NoAlpha().WithAlpha(altDown ? (int)(snapPointColor.Alpha / 2) : snapPointColor.Alpha)
);
}
g->BlendFilledEllipse(center + value.offset, { handleRadius, handleRadius }, foregroundColor.NoAlpha().WithAlpha((mouseHover || mouseDown) ? std::min(int(foregroundColor.Alpha * 1.5f), 255) : foregroundColor.Alpha));
g->BlendEllipse(center + value.offset, { handleRadius, handleRadius }, borderColor);
}
void DirectionSelector::OnMouseMoved(int x, int y, int dx, int dy)
{
if (mouseDown)
{
SetPositionAbs({ x, y });
}
CheckHovering(x, y);
}
void DirectionSelector::OnMouseClick(int x, int y, unsigned button)
{
mouseDown = true;
SetPositionAbs({ x, y });
CheckHovering(x, y);
}
void DirectionSelector::OnMouseUp(int x, int y, unsigned button)
{
mouseDown = false;
if (autoReturn)
{
SetPosition({ 0, 0 });
}
CheckHovering(x - Position.X, y - Position.Y);
if (changeCallback)
{
changeCallback(GetXValue(), GetYValue());
}
}
}