Use TouchGFX

This commit is contained in:
2023-03-06 21:21:00 +01:00
parent 2cadbff590
commit 8a2bdc347c
1214 changed files with 358250 additions and 87 deletions

View File

@ -0,0 +1,384 @@
/******************************************************************************
* 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/containers/scrollers/DrawableList.hpp>
namespace touchgfx
{
DrawableList::DrawableList()
: Container(),
isHorizontal(false),
isCircular(false),
offset(0),
itemSize(0),
itemMargin(0),
numItems(0),
numDrawables(0),
firstItem(0),
firstDrawable(0),
drawablesInitialized(false),
firstDrawableIndex(0),
drawableItems(0),
updateDrawable(0)
{
}
void DrawableList::setWidth(int16_t width)
{
Container::setWidth(width);
refreshDrawables();
}
void DrawableList::setHeight(int16_t height)
{
Container::setHeight(height);
refreshDrawables();
}
void DrawableList::setHorizontal(bool horizontal)
{
if ((horizontal && !isHorizontal) || (!horizontal && isHorizontal))
{
isHorizontal = horizontal;
refreshDrawables();
}
}
bool DrawableList::getHorizontal() const
{
return isHorizontal;
}
void DrawableList::setCircular(bool circular)
{
if ((circular && !isCircular) || (!circular && isCircular))
{
isCircular = circular;
refreshDrawables();
}
}
bool DrawableList::getCircular() const
{
return isCircular;
}
void DrawableList::setDrawableSize(int16_t drawableSize, int16_t drawableMargin)
{
itemSize = drawableSize + 2 * drawableMargin;
itemMargin = drawableMargin;
}
void DrawableList::setDrawables(DrawableListItemsInterface& drawableListItems,
int16_t drawableItemIndexOffset,
GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateDrawableCallback)
{
drawableItems = &drawableListItems;
firstDrawableIndex = drawableItemIndexOffset;
updateDrawable = &updateDrawableCallback;
refreshDrawables();
}
int16_t DrawableList::getNumberOfDrawables() const
{
return numDrawables;
}
int16_t DrawableList::getItemSize() const
{
return itemSize;
}
int16_t DrawableList::getDrawableSize() const
{
return itemSize - 2 * itemMargin;
}
int16_t DrawableList::getDrawableMargin() const
{
return itemMargin;
}
void DrawableList::setNumberOfItems(int16_t numberOfItems)
{
numItems = numberOfItems;
refreshDrawables();
}
int16_t DrawableList::getNumberOfItems() const
{
return numItems;
}
int16_t DrawableList::getRequiredNumberOfDrawables() const
{
if (drawableItems == 0 || itemSize <= 0)
{
return 0;
}
// Calculate number of required drawables. Worst case is one pixel visible of drawable at top and rest stacked tightly
int16_t requiredDrawables = 1 + (((isHorizontal ? getWidth() : getHeight()) - 1) + (itemSize - 1)) / itemSize;
if (!isCircular)
{
// We never require more drawables than the number of elements on non-circular list.
if (requiredDrawables > numItems)
{
requiredDrawables = numItems;
}
}
int16_t numberOfDrawables = drawableItems->getNumberOfDrawables();
return MIN((numberOfDrawables - firstDrawableIndex), requiredDrawables);
}
void DrawableList::setOffset(int32_t ofs)
{
offset = ofs;
if (numDrawables == 0 || numItems == 0 || itemSize == 0)
{
return;
}
if (!updateDrawable || !updateDrawable->isValid())
{
return;
}
// ofs is the offset of item[0]
// 0 => item[0] is perfectly selected, -itemSize => item[1] is perfectly selected, itemSize => item[N-1] is perfectly selected etc.
int16_t newFirstItem = 0;
if (ofs > 0)
{
int numberOfItems = ofs / itemSize + 1;
newFirstItem -= numberOfItems;
ofs -= numberOfItems * itemSize;
}
if (ofs <= -itemSize)
{
int numberOfItems = ofs / itemSize;
newFirstItem -= numberOfItems;
ofs -= numberOfItems * itemSize;
}
if (isCircular)
{
// Make sure that firstIndex is "in range"
newFirstItem %= numItems;
if (newFirstItem < 0)
{
newFirstItem += numItems;
}
}
else
{
if (newFirstItem < 0)
{
ofs -= newFirstItem * itemSize;
newFirstItem = 0;
}
else if (newFirstItem + numDrawables > numItems)
{
int x = numItems - (newFirstItem + numDrawables);
ofs += x * itemSize;
newFirstItem += x;
}
}
int drawableDelta = 0;
if (drawablesInitialized && firstItem != newFirstItem)
{
drawableDelta = numDrawables;
for (int i = 1; i < numDrawables; i++)
{
int fi = (firstItem + i);
int nfi = (newFirstItem + i);
if (isCircular)
{
fi %= numItems;
nfi %= numItems;
}
if (fi == newFirstItem)
{
drawableDelta = -i;
break;
}
if (nfi == firstItem)
{
drawableDelta = i;
break;
}
}
}
firstDrawable = ((firstDrawable - drawableDelta) + numDrawables) % numDrawables;
firstItem = newFirstItem;
for (int i = 0; i < numDrawables; i++)
{
int drawableIndex = (firstDrawable + i) % numDrawables;
Drawable* drawable = drawableItems->getDrawable(drawableIndex + firstDrawableIndex);
if (isHorizontal)
{
drawable->moveTo(ofs + i * itemSize + itemMargin, 0);
}
else
{
drawable->moveTo(0, ofs + i * itemSize + itemMargin);
}
int itemIndex = i + firstItem;
if (isCircular)
{
itemIndex %= numItems;
}
else
{
if (itemIndex < 0 || itemIndex >= numItems)
{
itemIndex = -1;
}
}
if (itemIndex < 0)
{
drawable->setVisible(false);
}
else
{
drawable->setVisible(true);
// Only fill if first time or outside old range
if (!drawablesInitialized || (i < drawableDelta || i >= numDrawables + drawableDelta))
{
if (updateDrawable->isValid())
{
updateDrawable->execute(drawableItems, drawableIndex + firstDrawableIndex, itemIndex);
drawable->invalidateContent();
}
}
}
}
drawablesInitialized = true;
}
int32_t DrawableList::getOffset() const
{
return offset;
}
int16_t DrawableList::getItemIndex(int16_t drawableIndex) const
{
if (drawableIndex < 0 || drawableIndex >= numDrawables || numDrawables == 0 || numItems == 0)
{
return -1;
}
int16_t itemNumber = ((drawableIndex + numDrawables - firstDrawable) % numDrawables) + firstItem;
if (isCircular)
{
itemNumber %= numItems;
}
if (itemNumber < 0 || itemNumber >= numItems)
{
return -1;
}
return itemNumber;
}
int16_t DrawableList::getDrawableIndices(int16_t itemIndex, int16_t* drawableIndexArray, int16_t arraySize) const
{
int16_t numFound = 0;
int16_t drawableIndex = -1;
while ((drawableIndex = getDrawableIndex(itemIndex, drawableIndex)) >= 0)
{
if (numFound < arraySize)
{
drawableIndexArray[numFound] = drawableIndex;
numFound++;
}
}
return numFound;
}
int16_t DrawableList::getDrawableIndex(int16_t itemIndex, int16_t prevDrawableIndex /*= -1*/) const
{
if (prevDrawableIndex < -1 || prevDrawableIndex >= numDrawables || numDrawables == 0 || numItems == 0)
{
return -1;
}
if (prevDrawableIndex >= 0)
{
prevDrawableIndex = ((prevDrawableIndex - firstDrawable) + numDrawables) % numDrawables;
}
for (int16_t i = prevDrawableIndex + 1; i < numDrawables; i++)
{
int16_t currentItemIndex = firstItem + i;
if (isCircular)
{
currentItemIndex %= numItems;
}
if (itemIndex == currentItemIndex)
{
int16_t drawableIndex = (firstDrawable + i) % numDrawables;
return drawableIndex;
}
}
return -1;
}
void DrawableList::refreshDrawables()
{
if (drawableItems == 0)
{
numDrawables = 0;
return;
}
numDrawables = getRequiredNumberOfDrawables();
// Remove everything
Container::removeAll();
// Add the itemDrawables
for (int drawableIndex = 0; drawableIndex < numDrawables; drawableIndex++)
{
Drawable* drawable = drawableItems->getDrawable(drawableIndex + firstDrawableIndex);
// Resize the drawables, X/Y ignored for now.
if (isHorizontal)
{
drawable->setPosition(0, 0, itemSize - 2 * itemMargin, getHeight());
}
else
{
drawable->setPosition(0, 0, getWidth(), itemSize - 2 * itemMargin);
}
// Add each drawable for later positioning
if (drawable->getParent() != 0)
{
// Remove drawable from the current parent
static_cast<Container*>(drawable->getParent())->remove(*drawable);
}
Container::add(*drawable);
}
drawablesInitialized = false;
firstItem = 0;
firstDrawable = 0;
setOffset(offset);
}
void DrawableList::itemChanged(int16_t itemIndex)
{
if (updateDrawable && updateDrawable->isValid())
{
int16_t drawableIndex = -1;
while ((drawableIndex = getDrawableIndex(itemIndex, drawableIndex)) != -1)
{
updateDrawable->execute(drawableItems, drawableIndex + firstDrawableIndex, itemIndex);
}
}
}
} // namespace touchgfx

View File

@ -0,0 +1,339 @@
/******************************************************************************
* 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/Application.hpp>
#include <touchgfx/Drawable.hpp>
#include <touchgfx/Utils.hpp>
#include <touchgfx/containers/scrollers/ScrollBase.hpp>
namespace touchgfx
{
ScrollBase::ScrollBase()
: Container(),
list(),
numberOfDrawables(0),
distanceBeforeAlignedItem(0),
itemSize(0),
swipeAcceleration(10),
dragAcceleration(10),
maxSwipeItems(0),
easingEquation(&EasingEquations::backEaseOut),
defaultAnimationSteps(30),
overshootPercentage(75),
itemSelectedCallback(0),
itemLockedInCallback(0),
animationEndedCallback(0),
itemPressedCallback(0),
currentAnimationState(NO_ANIMATION),
gestureStep(0),
gestureStepsTotal(0),
gestureStart(0),
gestureEnd(0),
xClick(0),
yClick(0),
initialSwipeOffset(0),
draggableX(false),
draggableY(false)
{
Container::add(list);
list.setXY(0, 0);
list.setHorizontal(false);
list.setCircular(false);
setTouchable(true);
}
void ScrollBase::setWidth(int16_t width)
{
Container::setWidth(width);
list.setWidth(width);
}
void ScrollBase::setHeight(int16_t height)
{
Container::setHeight(height);
list.setHeight(height);
}
void ScrollBase::setHorizontal(bool horizontal)
{
allowVerticalDrag(horizontal);
allowHorizontalDrag(!horizontal);
list.setHorizontal(horizontal);
}
bool ScrollBase::getHorizontal() const
{
return list.getHorizontal();
}
void ScrollBase::setCircular(bool circular)
{
list.setCircular(circular);
}
bool ScrollBase::getCircular() const
{
return list.getCircular();
}
void ScrollBase::setDrawableSize(int16_t drawableSize, int16_t drawableMargin)
{
itemSize = drawableSize + drawableMargin * 2;
list.setDrawableSize(drawableSize, drawableMargin);
}
int16_t ScrollBase::getDrawableSize() const
{
return list.getDrawableSize();
}
int16_t ScrollBase::getDrawableMargin() const
{
return list.getDrawableMargin();
}
void ScrollBase::setNumberOfItems(int16_t numberOfItems)
{
if (numberOfItems != getNumberOfItems())
{
list.setNumberOfItems(numberOfItems);
if (!getCircular())
{
animateToPosition(keepOffsetInsideLimits(getOffset(), 0));
}
}
}
int16_t ScrollBase::getNumberOfItems() const
{
return list.getNumberOfItems();
}
void ScrollBase::setEasingEquation(EasingEquation equation)
{
easingEquation = equation;
}
void ScrollBase::setAnimationSteps(int16_t steps)
{
defaultAnimationSteps = steps;
}
uint16_t ScrollBase::getAnimationSteps() const
{
return defaultAnimationSteps;
}
void ScrollBase::setSwipeAcceleration(uint16_t acceleration)
{
swipeAcceleration = acceleration;
}
uint16_t ScrollBase::getSwipeAcceleration() const
{
return swipeAcceleration;
}
void ScrollBase::setMaxSwipeItems(uint16_t maxItems)
{
maxSwipeItems = maxItems;
}
uint16_t ScrollBase::getMaxSwipeItems() const
{
return maxSwipeItems;
}
void ScrollBase::setDragAcceleration(uint16_t acceleration)
{
dragAcceleration = acceleration;
}
uint16_t ScrollBase::getDragAcceleration() const
{
return dragAcceleration;
}
void ScrollBase::allowHorizontalDrag(bool enable)
{
draggableX = enable;
}
void ScrollBase::allowVerticalDrag(bool enable)
{
draggableY = enable;
}
void ScrollBase::animateToItem(int16_t itemIndex, int16_t animationSteps /*= -1*/)
{
int32_t position = getPositionForItem(itemIndex);
animateToPosition(position, animationSteps);
}
void ScrollBase::setItemSelectedCallback(GenericCallback<int16_t>& callback)
{
itemSelectedCallback = &callback;
}
void ScrollBase::setAnimationEndedCallback(GenericCallback<>& callback)
{
animationEndedCallback = &callback;
}
void ScrollBase::setItemPressedCallback(GenericCallback<int16_t>& callback)
{
itemPressedCallback = &callback;
}
bool ScrollBase::isAnimating() const
{
return currentAnimationState != NO_ANIMATION;
}
void ScrollBase::stopAnimation()
{
if (currentAnimationState == ANIMATING_GESTURE)
{
Application::getInstance()->unregisterTimerWidget(this);
setOffset(gestureEnd);
}
currentAnimationState = NO_ANIMATION;
}
void ScrollBase::handleDragEvent(const DragEvent& event)
{
stopAnimation();
currentAnimationState = ANIMATING_DRAG;
int32_t newOffset = getOffset() + (getHorizontal() ? event.getDeltaX() : event.getDeltaY()) * dragAcceleration / 10;
newOffset = keepOffsetInsideLimits(newOffset, muldiv(itemSize, overshootPercentage, 100));
setOffset(newOffset);
}
void ScrollBase::handleGestureEvent(const GestureEvent& event)
{
if (event.getType() == (getHorizontal() ? GestureEvent::SWIPE_HORIZONTAL : GestureEvent::SWIPE_VERTICAL))
{
int16_t velocity = abs(event.getVelocity());
int16_t direction = event.getVelocity() < 0 ? -1 : 1;
int16_t steps = MAX(1, velocity - 4) * 7;
int32_t newOffset = getOffset() + direction * steps * swipeAcceleration / 10;
if (maxSwipeItems > 0)
{
int32_t maxDistance = maxSwipeItems * itemSize;
newOffset = MIN(newOffset, initialSwipeOffset + maxDistance);
newOffset = MAX(newOffset, initialSwipeOffset - maxDistance);
}
newOffset = keepOffsetInsideLimits(newOffset, 0);
steps = MIN(steps, defaultAnimationSteps);
animateToPosition(newOffset, steps);
}
}
void ScrollBase::handleTickEvent()
{
if (currentAnimationState == ANIMATING_GESTURE)
{
gestureStep++;
int newPosition = gestureStart + easingEquation(gestureStep, 0, gestureEnd - gestureStart, gestureStepsTotal);
setOffset(newPosition);
if (gestureStep > gestureStepsTotal)
{
currentAnimationState = NO_ANIMATION;
gestureStep = 0;
Application::getInstance()->unregisterTimerWidget(this);
setOffset(getNormalizedOffset(gestureEnd));
// Also adjust initialSwipeOffset in case it is being used.
initialSwipeOffset += getOffset() - gestureEnd;
// Item has settled, call back
if (animationEndedCallback && animationEndedCallback->isValid())
{
animationEndedCallback->execute();
}
}
}
}
void ScrollBase::itemChanged(int itemIndex)
{
list.itemChanged(itemIndex);
}
void ScrollBase::setOffset(int32_t offset)
{
list.setOffset(offset + distanceBeforeAlignedItem);
}
int32_t ScrollBase::getOffset() const
{
return list.getOffset() - distanceBeforeAlignedItem;
}
int ScrollBase::getNormalizedOffset(int offset) const
{
int16_t numItems = getNumberOfItems();
if (numItems == 0 || itemSize == 0)
{
return offset;
}
int32_t listSize = numItems * itemSize;
offset %= listSize;
return offset > 0 ? offset - listSize : offset;
}
int32_t ScrollBase::getNearestAlignedOffset(int32_t offset) const
{
if (itemSize == 0)
{
return offset;
}
if (getCircular())
{
if (offset < 0)
{
return (((offset - (itemSize / 2)) / itemSize) * itemSize);
}
return ((offset + (itemSize / 2)) / itemSize) * itemSize;
}
offset = keepOffsetInsideLimits(offset, 0);
return ((offset - (itemSize / 2)) / itemSize) * itemSize;
}
void ScrollBase::animateToPosition(int32_t position, int16_t steps)
{
int32_t currentPosition = getOffset();
position = getNearestAlignedOffset(position);
if (steps < 0)
{
steps = defaultAnimationSteps;
}
const int32_t distance = abs(position - currentPosition);
steps = MIN(steps, distance);
if (steps < 1)
{
setOffset(position);
currentAnimationState = NO_ANIMATION;
}
else
{
gestureStart = currentPosition;
gestureEnd = position;
gestureStep = 0;
gestureStepsTotal = steps;
if (currentAnimationState != ANIMATING_GESTURE)
{
Application::getInstance()->registerTimerWidget(this);
currentAnimationState = ANIMATING_GESTURE;
}
}
}
} // namespace touchgfx

View File

@ -0,0 +1,257 @@
/******************************************************************************
* 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/Utils.hpp>
#include <touchgfx/containers/scrollers/ScrollList.hpp>
namespace touchgfx
{
ScrollList::ScrollList()
: ScrollBase(),
paddingAfterLastItem(0),
snapping(false),
windowSize(1)
{
}
void ScrollList::setWidth(int16_t width)
{
ScrollBase::setWidth(width);
if (getHorizontal())
{
setWindowSize(windowSize);
}
}
void ScrollList::setHeight(int16_t height)
{
ScrollBase::setHeight(height);
if (!getHorizontal())
{
setWindowSize(windowSize);
}
}
void ScrollList::setDrawableSize(int16_t drawableSize, int16_t drawableMargin)
{
ScrollBase::setDrawableSize(drawableSize, drawableMargin);
setWindowSize(windowSize);
}
void ScrollList::setDrawables(DrawableListItemsInterface& drawableListItems, GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateDrawableCallback)
{
stopAnimation();
numberOfDrawables = drawableListItems.getNumberOfDrawables();
list.setDrawables(drawableListItems, 0, updateDrawableCallback);
setOffset(0);
}
void ScrollList::setWindowSize(int16_t items)
{
if (itemSize > 0)
{
const int16_t widgetSize = getHorizontal() ? getWidth() : getHeight();
const int16_t activeWidgetSize = widgetSize - (distanceBeforeAlignedItem + paddingAfterLastItem);
const int16_t numberOfVisibleItems = (activeWidgetSize + itemSize / 2) / itemSize; // Round up
items = MIN(items, numberOfVisibleItems); // No more than numberOfVisibleItems
}
windowSize = MAX(1, items); // No less than 1
animateToPosition(keepOffsetInsideLimits(getOffset(), 0));
}
void ScrollList::setPadding(int16_t paddingBefore, int16_t paddingAfter)
{
int32_t currentOffset = getOffset();
distanceBeforeAlignedItem = paddingBefore;
paddingAfterLastItem = paddingAfter;
setOffset(currentOffset);
list.refreshDrawables();
}
int16_t ScrollList::getPaddingBefore() const
{
return distanceBeforeAlignedItem;
}
int16_t ScrollList::getPaddingAfter() const
{
return paddingAfterLastItem;
}
void ScrollList::setSnapping(bool snap)
{
snapping = snap;
if (snapping)
{
setOffset(getNearestAlignedOffset(getOffset()));
}
}
bool ScrollList::getSnapping() const
{
return snapping;
}
int32_t ScrollList::getPositionForItem(int16_t itemIndex)
{
int32_t currentOffset = getNormalizedOffset(getOffset());
if (itemIndex < 0 || itemIndex >= list.getNumberOfItems() || itemSize == 0)
{
return currentOffset;
}
int32_t itemOffset = -itemIndex * itemSize;
// Get the visible size
const int16_t widgetSize = getHorizontal() ? getWidth() : getHeight();
const int16_t activeWidgetSize = widgetSize - (distanceBeforeAlignedItem + paddingAfterLastItem);
if (list.getCircular())
{
int32_t offset = currentOffset;
// Important this is a do-while of visibleSize < itemSize in which case we need to check at least one time
do
{
int16_t i = (-getNormalizedOffset(offset)) / itemSize; // Item index of first
if (itemIndex == i)
{
return currentOffset;
}
offset -= itemSize;
} while (offset >= currentOffset - (activeWidgetSize - itemSize));
int32_t allItemsSize = list.getNumberOfItems() * itemSize;
// Either scroll left from the first item or right from the last item. Find out which is closest
int32_t leftScrollDistance = itemOffset - currentOffset;
int32_t leftScrollDistance2 = (itemOffset + allItemsSize) - currentOffset;
int32_t rightItemOffset = getNormalizedOffset(currentOffset - (activeWidgetSize - itemSize));
int32_t rightScrollDistance = rightItemOffset - itemOffset;
int32_t rightScrollDistance2 = rightItemOffset - (itemOffset - allItemsSize);
if (abs(leftScrollDistance2) < abs(leftScrollDistance))
{
leftScrollDistance = leftScrollDistance2;
}
if (abs(rightScrollDistance2) < abs(rightScrollDistance))
{
rightScrollDistance = rightScrollDistance2;
}
if (abs(rightScrollDistance) < abs(leftScrollDistance))
{
return currentOffset - rightScrollDistance;
}
return currentOffset + leftScrollDistance;
}
if (itemOffset > currentOffset) // First item on screen is higher than the itemIndex. Scroll itemIndex to top position
{
return itemOffset;
}
const int16_t numberOfVisibleItems = activeWidgetSize / itemSize;
int32_t itemOffsetAtEnd = itemOffset;
if (numberOfVisibleItems > 0)
{
if (snapping)
{
itemOffsetAtEnd = itemOffset + (numberOfVisibleItems - 1) * itemSize;
}
else
{
itemOffsetAtEnd = itemOffset + activeWidgetSize - itemSize;
}
}
if (itemOffsetAtEnd < currentOffset)
{
return itemOffsetAtEnd;
}
return currentOffset;
}
void ScrollList::handleClickEvent(const ClickEvent& event)
{
ScrollBase::handleClickEvent(event);
if (event.getType() == ClickEvent::PRESSED)
{
xClick = event.getX();
yClick = event.getY();
initialSwipeOffset = getOffset();
setOffset(getNearestAlignedOffset(initialSwipeOffset));
if (itemPressedCallback && itemPressedCallback->isValid())
{
int16_t click = (getHorizontal() ? xClick : yClick);
int32_t offset = click - getOffset();
int32_t listSize = getNumberOfItems() * itemSize;
if (getCircular())
{
offset += listSize;
offset %= listSize;
}
if (offset >= 0 && offset < listSize)
{
int16_t item = offset / itemSize;
itemPressedCallback->execute(item);
}
}
}
else if (event.getType() == ClickEvent::RELEASED)
{
if (currentAnimationState == NO_ANIMATION)
{
// For a tiny drag, start by re-aligning (no animation(!))
setOffset(getNearestAlignedOffset(getOffset()));
if (itemSelectedCallback && itemSelectedCallback->isValid())
{
int16_t click = (getHorizontal() ? xClick : yClick);
int32_t offset = click - getOffset();
int32_t listSize = getNumberOfItems() * itemSize;
if (getCircular())
{
offset += listSize;
offset %= listSize;
}
else
{
offset -= distanceBeforeAlignedItem;
}
if (offset >= 0 && offset < listSize)
{
int16_t item = offset / itemSize;
itemSelectedCallback->execute(item);
}
}
}
else if (currentAnimationState == ANIMATING_DRAG)
{
// click + drag + release. Find best Y to scroll to
animateToPosition(getNearestAlignedOffset(getOffset()));
}
}
}
int32_t ScrollList::getNearestAlignedOffset(int32_t offset) const
{
if (snapping)
{
// ScrollBase implementation will snap
return ScrollBase::getNearestAlignedOffset(offset);
}
return keepOffsetInsideLimits(offset, 0);
}
int32_t ScrollList::keepOffsetInsideLimits(int32_t newOffset, int16_t overShoot) const
{
if (!getCircular())
{
newOffset = MIN(newOffset, overShoot);
int maxOffToTheStart = windowSize < getNumberOfItems() ? getNumberOfItems() - windowSize : 0;
newOffset = MAX(newOffset, -(itemSize * maxOffToTheStart) - overShoot);
}
return newOffset;
}
} // namespace touchgfx

View File

@ -0,0 +1,26 @@
/******************************************************************************
* 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/containers/scrollers/ScrollWheel.hpp>
namespace touchgfx
{
void ScrollWheel::setDrawables(DrawableListItemsInterface& drawableListItems, GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateDrawableCallback)
{
stopAnimation();
numberOfDrawables = drawableListItems.getNumberOfDrawables();
list.setDrawables(drawableListItems, 0, updateDrawableCallback);
setOffset(0);
}
} // namespace touchgfx

View File

@ -0,0 +1,177 @@
/******************************************************************************
* 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/Utils.hpp>
#include <touchgfx/containers/scrollers/ScrollWheelBase.hpp>
namespace touchgfx
{
ScrollWheelBase::ScrollWheelBase()
: ScrollBase(),
animateToCallback(0)
{
ScrollWheelBase::setHorizontal(false);
setTouchable(true);
}
void ScrollWheelBase::setSelectedItemOffset(int16_t offset)
{
int32_t currentOffset = getOffset();
distanceBeforeAlignedItem = offset;
setOffset(currentOffset);
}
int16_t ScrollWheelBase::getSelectedItemOffset() const
{
return distanceBeforeAlignedItem;
}
int32_t ScrollWheelBase::getPositionForItem(int16_t itemIndex)
{
int32_t newOffset = -itemIndex * itemSize;
if (getCircular())
{
// Check if it is closer to scroll backwards
int32_t otherOffset = newOffset + getNumberOfItems() * itemSize;
int32_t offset = getOffset();
if (abs(otherOffset - offset) < abs(newOffset - offset))
{
newOffset = otherOffset;
}
}
return newOffset;
}
void ScrollWheelBase::animateToPosition(int32_t position, int16_t steps)
{
if (itemSize == 0)
{
return;
}
if (animateToCallback && animateToCallback->isValid() && itemSize > 0)
{
position = getNearestAlignedOffset(position);
int16_t itemIndex = (-position) / itemSize;
animateToCallback->execute(itemIndex);
}
ScrollBase::animateToPosition(position, steps);
}
int ScrollWheelBase::getSelectedItem() const
{
if (itemSize == 0)
{
return 0;
}
if (currentAnimationState == ANIMATING_GESTURE)
{
// Scroll in progress, get the destination value
return (-getNormalizedOffset(gestureEnd)) / itemSize;
}
return (-getNormalizedOffset(getOffset())) / itemSize;
}
int32_t ScrollWheelBase::keepOffsetInsideLimits(int32_t newOffset, int16_t overShoot) const
{
if (!getCircular())
{
newOffset = MIN(newOffset, overShoot);
int16_t numberOfItems = getNumberOfItems();
newOffset = MAX(newOffset, -(itemSize * (numberOfItems - 1)) - overShoot);
}
return newOffset;
}
void ScrollWheelBase::handleClickEvent(const ClickEvent& event)
{
if (itemSize == 0)
{
return;
}
int32_t offset = getOffset();
if (event.getType() == ClickEvent::PRESSED)
{
xClick = event.getX();
yClick = event.getY();
initialSwipeOffset = offset;
if (itemPressedCallback && itemPressedCallback->isValid())
{
itemPressedCallback->execute(getSelectedItem());
}
}
else if (event.getType() == ClickEvent::RELEASED)
{
if (currentAnimationState == NO_ANIMATION)
{
int16_t click = getHorizontal() ? xClick : yClick;
// Click => move to clicked position
if (click < distanceBeforeAlignedItem)
{
animateToPosition(offset + ((distanceBeforeAlignedItem - click) / itemSize + 1) * itemSize);
}
else if (click > distanceBeforeAlignedItem + itemSize)
{
animateToPosition(offset - ((click - distanceBeforeAlignedItem) / itemSize) * itemSize);
}
else
{
animateToPosition(offset);
}
}
else if (currentAnimationState == ANIMATING_DRAG)
{
// click + drag + release. Find best Y to scroll to
animateToPosition(offset);
}
if (itemSelectedCallback && itemSelectedCallback->isValid())
{
itemSelectedCallback->execute(getSelectedItem());
}
}
}
void ScrollWheelBase::handleDragEvent(const DragEvent& event)
{
currentAnimationState = ANIMATING_DRAG;
int newOffset = getOffset() + (getHorizontal() ? event.getDeltaX() : event.getDeltaY()) * dragAcceleration / 10;
if (!getCircular())
{
newOffset = MIN(newOffset, itemSize * 3 / 4);
int16_t numberOfItems = getNumberOfItems();
newOffset = MAX(newOffset, -(itemSize * (numberOfItems - 1)) - itemSize * 3 / 4);
}
setOffset(newOffset);
}
void ScrollWheelBase::handleGestureEvent(const GestureEvent& event)
{
if (event.getType() == (getHorizontal() ? GestureEvent::SWIPE_HORIZONTAL : GestureEvent::SWIPE_VERTICAL))
{
int32_t newOffset = getOffset() + event.getVelocity() * swipeAcceleration / 10;
if (maxSwipeItems > 0)
{
int32_t maxDistance = maxSwipeItems * itemSize;
newOffset = MIN(newOffset, initialSwipeOffset + maxDistance);
newOffset = MAX(newOffset, initialSwipeOffset - maxDistance);
}
animateToPosition(newOffset);
}
}
void ScrollWheelBase::setAnimateToCallback(GenericCallback<int16_t>& callback)
{
animateToCallback = &callback;
}
} // namespace touchgfx

View File

@ -0,0 +1,202 @@
/******************************************************************************
* 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/containers/scrollers/ScrollWheelWithSelectionStyle.hpp>
namespace touchgfx
{
ScrollWheelWithSelectionStyle::ScrollWheelWithSelectionStyle()
: ScrollWheelBase(),
drawablesInFirstList(0),
list1(),
list2(),
extraSizeBeforeSelectedItem(0),
extraSizeAfterSelectedItem(0),
marginBeforeSelectedItem(0),
marginAfterSelectedItem(0),
drawables(0),
centerDrawables(0),
originalUpdateDrawableCallback(0),
originalUpdateCenterDrawableCallback(0)
{
ScrollWheelBase::add(list2);
ScrollWheelBase::add(list1); // Put center list at top of the first/last list.
}
void ScrollWheelWithSelectionStyle::setWidth(int16_t width)
{
ScrollWheelBase::setWidth(width);
if (getHorizontal())
{
refreshDrawableListsLayout();
}
}
void ScrollWheelWithSelectionStyle::setHeight(int16_t height)
{
ScrollWheelBase::setHeight(height);
if (!getHorizontal())
{
refreshDrawableListsLayout();
}
}
void ScrollWheelWithSelectionStyle::setHorizontal(bool horizontal)
{
ScrollWheelBase::setHorizontal(horizontal);
list1.setHorizontal(horizontal);
list2.setHorizontal(horizontal);
refreshDrawableListsLayout();
}
void ScrollWheelWithSelectionStyle::setCircular(bool circular)
{
ScrollWheelBase::setCircular(circular);
list1.setCircular(circular);
list2.setCircular(circular);
}
void ScrollWheelWithSelectionStyle::setNumberOfItems(int16_t numberOfItems)
{
if (numberOfItems != getNumberOfItems())
{
ScrollWheelBase::setNumberOfItems(numberOfItems);
list1.setNumberOfItems(numberOfItems);
list2.setNumberOfItems(numberOfItems);
}
}
void ScrollWheelWithSelectionStyle::setSelectedItemOffset(int16_t offset)
{
ScrollWheelBase::setSelectedItemOffset(offset);
refreshDrawableListsLayout();
}
void ScrollWheelWithSelectionStyle::setSelectedItemExtraSize(int16_t extraSizeBefore, int16_t extraSizeAfter)
{
extraSizeBeforeSelectedItem = extraSizeBefore;
extraSizeAfterSelectedItem = extraSizeAfter;
refreshDrawableListsLayout();
}
int16_t ScrollWheelWithSelectionStyle::getSelectedItemExtraSizeBefore() const
{
return extraSizeBeforeSelectedItem;
}
int16_t ScrollWheelWithSelectionStyle::getSelectedItemExtraSizeAfter() const
{
return extraSizeAfterSelectedItem;
}
void ScrollWheelWithSelectionStyle::setSelectedItemMargin(int16_t marginBefore, int16_t marginAfter)
{
marginBeforeSelectedItem = marginBefore;
marginAfterSelectedItem = marginAfter;
refreshDrawableListsLayout();
}
int16_t ScrollWheelWithSelectionStyle::getSelectedItemMarginBefore() const
{
return marginBeforeSelectedItem;
}
int16_t ScrollWheelWithSelectionStyle::getSelectedItemMarginAfter() const
{
return marginAfterSelectedItem;
}
void ScrollWheelWithSelectionStyle::setSelectedItemPosition(int16_t offset, int16_t extraSizeBefore, int16_t extraSizeAfter, int16_t marginBefore, int16_t marginAfter)
{
setSelectedItemOffset(offset);
setSelectedItemExtraSize(extraSizeBefore, extraSizeAfter);
setSelectedItemMargin(marginBefore, marginAfter);
}
void ScrollWheelWithSelectionStyle::setDrawableSize(int16_t drawableSize, int16_t drawableMargin)
{
ScrollWheelBase::setDrawableSize(drawableSize, drawableMargin);
list1.setDrawableSize(drawableSize, drawableMargin);
list2.setDrawableSize(drawableSize, drawableMargin);
// Resize the three lists
setSelectedItemOffset(distanceBeforeAlignedItem);
// Changing the drawable size alters number of required drawables, so refresh this
refreshDrawableListsLayout();
}
void ScrollWheelWithSelectionStyle::setDrawables(DrawableListItemsInterface& drawableListItems, GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateDrawableCallback,
DrawableListItemsInterface& centerDrawableListItems, GenericCallback<DrawableListItemsInterface*, int16_t, int16_t>& updateCenterDrawableCallback)
{
drawables = &drawableListItems;
centerDrawables = &centerDrawableListItems;
currentAnimationState = NO_ANIMATION;
originalUpdateDrawableCallback = &updateDrawableCallback;
originalUpdateCenterDrawableCallback = &updateCenterDrawableCallback;
refreshDrawableListsLayout();
setOffset(0);
}
void ScrollWheelWithSelectionStyle::setOffset(int32_t offset)
{
ScrollWheelBase::setOffset(offset);
list1.setOffset((distanceBeforeAlignedItem - (distanceBeforeAlignedItem - extraSizeBeforeSelectedItem)) + offset);
list2.setOffset((distanceBeforeAlignedItem - (distanceBeforeAlignedItem + itemSize + extraSizeAfterSelectedItem + marginAfterSelectedItem)) + offset);
}
void ScrollWheelWithSelectionStyle::itemChanged(int itemIndex)
{
ScrollWheelBase::itemChanged(itemIndex);
list1.itemChanged(itemIndex);
list2.itemChanged(itemIndex);
}
void ScrollWheelWithSelectionStyle::refreshDrawableListsLayout()
{
if (drawables != 0 && centerDrawables != 0)
{
int32_t currentOffset = getOffset();
int16_t list1Pos = distanceBeforeAlignedItem - extraSizeBeforeSelectedItem;
int16_t list2Pos = distanceBeforeAlignedItem + itemSize + (extraSizeAfterSelectedItem + marginAfterSelectedItem);
int16_t list0Size = list1Pos - marginBeforeSelectedItem;
int16_t list1Size = itemSize + extraSizeBeforeSelectedItem + extraSizeAfterSelectedItem;
if (getHorizontal())
{
int16_t list2Size = getWidth() - list2Pos;
list.setPosition(0, 0, list0Size, getHeight());
list1.setPosition(list1Pos, 0, list1Size, getHeight());
list2.setPosition(list2Pos, 0, list2Size, getHeight());
}
else
{
int16_t list2Size = getHeight() - list2Pos;
list.setPosition(0, 0, getWidth(), list0Size);
list1.setPosition(0, list1Pos, getWidth(), list1Size);
list2.setPosition(0, list2Pos, getWidth(), list2Size);
}
list.setDrawables(*drawables, 0, *originalUpdateDrawableCallback);
drawablesInFirstList = list.getNumberOfDrawables();
list1.setDrawables(*centerDrawables, 0, *originalUpdateCenterDrawableCallback);
list2.setDrawables(*drawables, drawablesInFirstList, *originalUpdateDrawableCallback);
setOffset(keepOffsetInsideLimits(currentOffset, 0));
}
}
} // namespace touchgfx