CircularSlider.cpp Example File
circularslider/src/CircularSlider.cpp
#include "CircularSlider.hpp"
#include <bb/cascades/AbsoluteLayout>
#include <bb/cascades/Container>
#include <bb/cascades/DockLayout>
#include <bb/cascades/ImageView>
#include <bb/cascades/TouchEvent>
#include "math.h"
using namespace bb::cascades;
CircularSlider::CircularSlider(Container *parent)
: CustomControl(parent)
, m_revAngle(0)
, m_angle(0)
, m_value(0)
{
m_rootContainer = new Container();
m_rootContainer->setLayout(new AbsoluteLayout());
m_trackImage = ImageView::create().image(QUrl("asset:///images/slider_track.png"));
m_handleContainer = Container::create().layout(new DockLayout());
m_handleImplicitAnimationController = ImplicitAnimationController::create(m_handleContainer).enabled(false);
m_handleOn = Image(QUrl("asset:///images/handle_pressed.png"));
m_handleOff = Image(QUrl("asset:///images/handle_inactive.png"));
m_handle = ImageView::create().image(m_handleOff)
.horizontal(HorizontalAlignment::Right)
.vertical(VerticalAlignment::Center);
m_handleContainer->add(m_handle);
m_rootContainer->add(m_trackImage);
m_rootContainer->add(m_handleContainer);
setRoot(m_rootContainer);
bool ok = connect(this, SIGNAL(preferredHeightChanged(float)), this, SLOT(onHeightChanged(float)));
Q_ASSERT(ok);
ok = connect(this, SIGNAL(preferredWidthChanged(float)), this, SLOT(onWidthChanged(float)));
Q_ASSERT(ok);
m_width = 600;
m_height = 600;
setPreferredSize(m_width, m_height);
ok = connect(m_rootContainer, SIGNAL(touch(bb::cascades::TouchEvent*)),
this, SLOT(onSliderHandleTouched(bb::cascades::TouchEvent*)));
Q_ASSERT(ok);
}
void CircularSlider::onWidthChanged(float width)
{
m_width = width;
onSizeChanged();
}
void CircularSlider::onHeightChanged(float height)
{
m_height = height;
onSizeChanged();
}
void CircularSlider::onSizeChanged()
{
m_centerX = m_width / 2;
m_centerY = m_height / 2;
m_radiusCircle = m_width - m_centerX;
m_rootContainer->setPreferredSize(m_width, m_height);
m_trackImage->setPreferredSize(m_width * 0.85, m_height * 0.85);
m_handle->setPreferredSize(0.2 * m_width, 0.2 * m_height);
m_handleContainer->setPreferredSize(m_width, 0.2 * m_height);
m_handleContainer->setTranslationY((m_height - 0.2 * m_height) / 2);
m_trackImage->setTranslation((m_width - 0.85 * m_width) / 2,
(m_height - 0.85 * m_height) / 2);
m_pointsOnCircumference.clear();
for (int angle = 0; angle < 360; angle++) {
const float x = m_centerX + (m_radiusCircle) * cos(angle * M_PI / 180);
const float y = m_centerY + (m_radiusCircle) * sin(angle * M_PI / 180);
m_pointsOnCircumference.append(qMakePair(x, y));
}
}
void CircularSlider::processRawCoordinates(float touchX, float touchY)
{
const float distanceFromCenterToTouchPoint = sqrt(
(touchX - m_centerX) * (touchX - m_centerX) +
(touchY - m_centerY) * (touchY - m_centerY));
if (distanceFromCenterToTouchPoint >= (0.3 * m_radiusCircle) &&
distanceFromCenterToTouchPoint <= m_radiusCircle) {
float minDistanceFromTouch = INT_MAX;
for (float i = 0; i < m_pointsOnCircumference.size(); i++) {
const float x = m_pointsOnCircumference[i].first;
const float y = m_pointsOnCircumference[i].second;
const float distanceFromTouch = sqrt((x - touchX) * (x - touchX) +
(y - touchY) * (y - touchY));
if (distanceFromTouch < minDistanceFromTouch) {
minDistanceFromTouch = distanceFromTouch;
m_angle = i;
}
}
if (m_angle != m_revAngle) {
m_handleContainer->setRotationZ(m_angle);
m_value = m_angle;
emit valueChanged(m_value);
m_revAngle = m_angle;
}
}
}
float CircularSlider::value() const
{
return m_value;
}
void CircularSlider::setValue(float value)
{
if (m_value == value)
return;
m_revAngle = m_angle = m_value = value;
m_handleContainer->setRotationZ(m_angle);
emit valueChanged(m_value);
}
void CircularSlider::onSliderHandleTouched(TouchEvent* touchEvent)
{
if (touchEvent->isDown()) {
m_handle->setImage(m_handleOn);
} else if (touchEvent->isUp()) {
m_handle->setImage(m_handleOff);
} else if (touchEvent->isMove()) {
processRawCoordinates(touchEvent->localX(), touchEvent->localY());
}
}