298 lines
9.5 KiB
C++

/******************************************************************************
* Copyright (c) 2018(-2023) STMicroelectronics.
* All rights reserved.
*
* This file is part of the TouchGFX 4.21.2 distribution.
*
* This software is licensed under terms that can be found in the LICENSE file in
* the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
*******************************************************************************/
#include <touchgfx/Drawable.hpp>
#include <touchgfx/containers/Slider.hpp>
namespace touchgfx
{
Slider::Slider()
: Container(),
sliderOrientation(HORIZONTAL),
currentValue(0),
valueRangeMin(0),
valueRangeMax(1),
background(),
backgroundSelected(),
indicator(),
backgroundSelectedViewPort(),
indicatorMinPosition(0),
indicatorMaxPosition(1),
startValueCallback(0),
stopValueCallback(0),
newValueCallback(0)
{
setTouchable(true);
// The backgroundSelectedViewPort is a container into which the bitmap for the "filled" background
// is placed. Containers are viewports, so the dimensions of this container controls how
// much of the filled background is visible.
backgroundSelectedViewPort.add(backgroundSelected);
Container::add(background);
Container::add(backgroundSelectedViewPort);
Container::add(indicator);
// Default value range
Slider::setValueRange(0, 100);
}
void Slider::setBitmaps(const Bitmap& sliderBackground, const Bitmap& sliderBackgroundSelected, const Bitmap& indicatorBitmap)
{
assert(sliderBackground.getWidth() == sliderBackgroundSelected.getWidth() &&
sliderBackground.getHeight() == sliderBackgroundSelected.getHeight() &&
"Slider::setBitmaps - background and backgroundFilled must have same dimensions");
background.setBitmap(sliderBackground);
backgroundSelected.setBitmap(sliderBackgroundSelected);
indicator.setBitmap(indicatorBitmap);
backgroundSelectedViewPort.setWidthHeight(backgroundSelected);
}
void Slider::setBitmaps(const BitmapId sliderBackground, const BitmapId sliderBackgroundSelected, const BitmapId indicatorBitmap)
{
setBitmaps(Bitmap(sliderBackground), Bitmap(sliderBackgroundSelected), Bitmap(indicatorBitmap));
}
void Slider::setupHorizontalSlider(uint16_t backgroundX, uint16_t backgroundY, uint16_t indicatorY, uint16_t indicatorMinX, uint16_t indicatorMaxX)
{
assert(indicatorMinX < indicatorMaxX && "Slider::setupHorizontalSlider - indicatorMinX must be smaller than indicatorMaxX");
sliderOrientation = HORIZONTAL;
background.setXY(backgroundX, backgroundY);
backgroundSelectedViewPort.setXY(backgroundX, backgroundY);
backgroundSelected.setXY(0, 0);
indicator.setY(indicatorY);
uint16_t backgroundWidth = backgroundX + static_cast<uint16_t>(background.getWidth());
uint16_t indicatorWidth = indicatorMaxX + static_cast<uint16_t>(indicator.getWidth());
int16_t newWidth = static_cast<int16_t>(MAX(backgroundWidth, indicatorWidth));
uint16_t backgroundHeight = backgroundY + static_cast<uint16_t>(background.getHeight());
uint16_t indicatorHeight = indicatorY + static_cast<uint16_t>(indicator.getHeight());
int16_t newHeight = static_cast<int16_t>(MAX(backgroundHeight, indicatorHeight));
indicatorMinPosition = indicatorMinX;
indicatorMaxPosition = indicatorMaxX;
setWidthHeight(newWidth, newHeight);
setValue(currentValue);
}
void Slider::setupVerticalSlider(uint16_t backgroundX, uint16_t backgroundY, uint16_t indicatorX, uint16_t indicatorMinY, uint16_t indicatorMaxY)
{
assert(indicatorMinY < indicatorMaxY && "Slider::setupVerticalSlider - indicatorMinY must be smaller than indicatorMaxY");
sliderOrientation = VERTICAL;
background.setXY(backgroundX, backgroundY);
backgroundSelectedViewPort.setXY(backgroundX, backgroundY);
indicator.setX(indicatorX);
uint16_t backgroundWidth = backgroundX + static_cast<uint16_t>(background.getWidth());
uint16_t indicatorWidth = indicatorX + static_cast<uint16_t>(indicator.getWidth());
int16_t newWidth = static_cast<int16_t>(MAX(backgroundWidth, indicatorWidth));
uint16_t backgroundHeight = backgroundY + static_cast<uint16_t>(background.getHeight());
uint16_t indicatorHeight = indicatorMaxY + static_cast<uint16_t>(indicator.getHeight());
int16_t newHeight = static_cast<int16_t>(MAX(backgroundHeight, indicatorHeight));
indicatorMinPosition = indicatorMinY;
indicatorMaxPosition = indicatorMaxY;
setWidthHeight(newWidth, newHeight);
setValue(currentValue);
}
void Slider::setValue(int value)
{
updateIndicatorPosition(valueToPosition(value));
}
void Slider::handleClickEvent(const ClickEvent& event)
{
if ((event.getType() == ClickEvent::PRESSED) || (event.getType() == ClickEvent::RELEASED))
{
// Communicate the start value if a listener is registered
if ((event.getType() == ClickEvent::PRESSED) && (startValueCallback != 0) && startValueCallback->isValid())
{
startValueCallback->execute(*this, currentValue);
}
if (sliderOrientation == HORIZONTAL)
{
updateIndicatorPosition(event.getX() - getIndicatorRadius());
}
else
{
updateIndicatorPosition(event.getY() - getIndicatorRadius());
}
// Communicate the stop value if a listener is registered
if ((event.getType() == ClickEvent::RELEASED) && (stopValueCallback != 0) && stopValueCallback->isValid())
{
stopValueCallback->execute(*this, currentValue);
}
}
}
void Slider::handleDragEvent(const DragEvent& event)
{
if (sliderOrientation == HORIZONTAL)
{
updateIndicatorPosition(event.getNewX() - getIndicatorRadius());
}
else
{
updateIndicatorPosition(event.getNewY() - getIndicatorRadius());
}
}
int16_t Slider::valueToPosition(int value) const
{
value = MIN(valueRangeMax, value);
value = MAX(value, valueRangeMin);
int coordinateOffset = ((value - valueRangeMin) * (getIndicatorPositionRangeSize() + 1)) / getValueRangeSize();
int result = indicatorMinPosition + coordinateOffset;
if (sliderOrientation == VERTICAL)
{
// Vertical slider grows as the position decreases so invert the coordinate
result = indicatorMinPosition + (indicatorMaxPosition - result);
}
return result;
}
int Slider::positionToValue(int16_t position) const
{
int result;
if (position == indicatorMinPosition)
{
// Ensure that min coordinate always results in min value
result = valueRangeMin;
}
else if (position == indicatorMaxPosition)
{
// Ensure that max coordinate always results in max value
result = valueRangeMax;
}
else
{
int rounding = getIndicatorPositionRangeSize() / 2;
int valueOffset = (((position - indicatorMinPosition) * getValueRangeSize()) + rounding) / getIndicatorPositionRangeSize();
result = valueRangeMin + valueOffset;
}
if (sliderOrientation == VERTICAL)
{
// Vertical slider grows as the position decreases so invert the value
result = valueRangeMin + (valueRangeMax - result);
}
return result;
}
void Slider::updateIndicatorPosition(int16_t position)
{
// Cut off positions outside the slider area
position = MAX(position, indicatorMinPosition);
position = MIN(position, indicatorMaxPosition);
if (sliderOrientation == HORIZONTAL)
{
indicator.moveTo(position, indicator.getY());
backgroundSelectedViewPort.invalidate();
backgroundSelectedViewPort.setWidth((position - backgroundSelectedViewPort.getX()) + getIndicatorRadius());
backgroundSelectedViewPort.invalidate();
}
else
{
indicator.moveTo(indicator.getX(), position);
backgroundSelectedViewPort.invalidate();
int16_t newViewPortHeight = background.getRect().bottom() - (position + getIndicatorRadius());
backgroundSelectedViewPort.setPosition(backgroundSelectedViewPort.getX(), position + getIndicatorRadius(), backgroundSelectedViewPort.getWidth(), newViewPortHeight);
backgroundSelected.setY(-(backgroundSelected.getHeight() - newViewPortHeight));
backgroundSelectedViewPort.invalidate();
}
currentValue = positionToValue(position);
// Communicate the new value if a listener is registered
if ((newValueCallback != 0) && newValueCallback->isValid())
{
newValueCallback->execute(*this, currentValue);
}
}
uint16_t Slider::getIndicatorRadius() const
{
uint16_t result;
if (sliderOrientation == HORIZONTAL)
{
result = indicator.getWidth() / 2;
}
else
{
result = indicator.getHeight() / 2;
}
return result;
}
void Slider::setValueRange(int minValue, int maxValue, int newValue)
{
assert(minValue < maxValue && "Slider::setValueRange - minValue must be smaller than maxValue");
valueRangeMin = minValue;
valueRangeMax = maxValue;
setValue(newValue);
}
void Slider::setValueRange(int minValue, int maxValue)
{
int newValue = currentValue;
if (currentValue < minValue)
{
newValue = minValue;
}
else if (currentValue > maxValue)
{
newValue = maxValue;
}
setValueRange(minValue, maxValue, newValue);
}
int Slider::getIndicatorPositionRangeSize() const
{
return indicatorMaxPosition - indicatorMinPosition;
}
int Slider::getValueRangeSize() const
{
return valueRangeMax - valueRangeMin;
}
} // namespace touchgfx