Skip to main content
Tnguy.3
Associate II
November 11, 2021
Solved

Scroll list how to handle click and drag for better experience?

  • November 11, 2021
  • 4 replies
  • 4186 views

I'm currently using a scroll list on one of my view, and for the item, there is a button to transition the user to another screen. But that button is messing up the smoothness of the drag-scroll, so I followed this thread

https://community.st.com/s/question/0D50X0000AIcwHv/scroll-list-with-clickable-items

and got it differentiate between a click and drag by overriding the click and drag events.

void Screen2View::handleClickEvent(const ClickEvent& evt)
{
 if (isClicked && evt.getType() == ClickEvent::RELEASED)
 {
 touchgfx_printf("Click detected\n");
 scrollWheelListItems.element->setTouchable(true); // not working
 scrollWheel.handleClickEvent(evt); // not working either
 Screen2ViewBase::handleClickEvent(evt); // nore this
 isClicked = false;
 }
 else
 isClicked = true;
}
 
void Screen2View::handleDragEvent(const DragEvent& evt)
{
 //touchgfx_printf("dragged detected ");
 scrollWheel.handleDragEvent(evt);
 isClicked = false;
}

Now, the problem I'm facing is that because I override the click event, the button doesn't work anymore. How can I send the click event to the item's button so that it can do what is needed?

Edit: I want to say that those statements inside the if does get hit (triggers my breakpoint)

This topic has been closed for replies.
Best answer by Tnguy.3

I found a working solution base on the example projects.

In my view's hpp file,

class MenuView : public MenuViewBase
{
public:
 MenuView();
 virtual ~MenuView() {}
 virtual void setupScreen();
 virtual void tearDownScreen();
 virtual void handleDragEvent(const DragEvent& evt); // Need to override this to set our flag (below)
 ...
protected:
private:
 bool isClicked = true; // need a flag to differentiate clicks and drags
 
 // Need to declare a callback for item selection (who knew these existed???)
 touchgfx::Callback<MenuView, touchgfx::DrawableListItemsInterface*, int16_t, int16_t> updateItemCallback;
 void updateItemCallbackHandler(touchgfx::DrawableListItemsInterface* items, int16_t containerIndex, int16_t itemIndex);
 ...
};

and then in my cpp:

MenuView::MenuView():
 scrollListItemSelectedCallback(this, &MenuView::scrollListItemSelectedHandler),
 // Connect our item select callback to the handler
 ...
{
 ...
}
 
void MenuView::handleDragEvent(const DragEvent& evt)
{
 if (evt.getDeltaY() != 0 || evt.getDeltaX() != 0)
 {
 // Check to see if the user moved X or Y since touch was detected, if not then it's a click
 isClicked = false;
 }
 else
 {
 isClicked = true;
 }
 MenuViewBase::handleDragEvent(evt); // IMPORTANT: call back to the base so that it handles the drag/scroll
}
 
void MenuView::scrollListItemSelectedHandler(int16_t itemSelected)
{
 // We only want it to register the "Select" if the condition met.
 if (isClicked && itemSelected >= 0 && itemSelected < MenuScrollWheelListItems.getNumberOfDrawables())
 {
 touchgfx::Drawable* d = MenuScrollWheelListItems.getDrawable(itemSelected);
 MenuItem* cc = (MenuItem*)d;
 // Since the itemSelected return the wrong index we needed, we'll get the correct index from our custom public getItemIndex function and update our cc item to match it (it's weird, IK).
 *cc = MenuScrollWheelListItems.element[cc->getItemIndex()];
 cc->onItemClicked(); // a public function in the item (this case it's for MenuItem container) class
 }
 else
 isClicked = true; // reset our flag
}

Hope this helps anyone in the future

4 replies

GMeur
Associate III
November 12, 2021
template<class Base>
class ScrollListClickableElement : public Base
{
public:
	ScrollListClickableElement() : _click(touchgfx::ClickEvent::PRESSED, 0, 0) {};
 
 void setParentContainer(touchgfx::Container* parent_container) { _parent_container = parent_container; }
 void handleClickEvent(const touchgfx::ClickEvent& event) override
 {
 _drag = false;
 _click = event;
 if (_parent_container)
 _parent_container->handleClickEvent(event);
 
 Base::handleClickEvent(event);
 }
 void handleDragEvent(const touchgfx::DragEvent& event) override
 {
 if (_parent_container)
 {
 //para cancelar el click
			if (std::abs(event.getDeltaY()) > 5)
 	{
				if (!_drag)
				{ 
					_click.setType(touchgfx::ClickEvent::CANCEL);				 
					Base::handleClickEvent(_click);
					_drag = true;
				}
			}
 _parent_container->handleDragEvent(event);
 }
 }
 
 void handleGestureEvent(const touchgfx::GestureEvent& event) override
 {
 if (_parent_container)
 _parent_container->handleGestureEvent(event);
 }
private:
 touchgfx::Container* _parent_container = nullptr;
 touchgfx::ClickEvent _click;
 bool _drag = false;
};

Here's my solution to your problem. Each element of your scrollList must be of the type ScrollListClickableElement. So if you want to make a scrollList of buttons, your scrollList must actually be of the type ScrollListClickableElement<Button>. Then, you connect your ScrollList to each element with the function setParentContainer (so it's setParentContainer(&scrollList) and voilà ! The big advantage of doing it like that is that you don't have to overwrite the view handleClick and handleDrag functions.

Tnguy.3
Tnguy.3Author
Associate II
November 12, 2021

Thanks for your answer! I'm still new to touchGFX and was wondering where or how I can change the type to ScrollListClickableElement you're referring to?

I see in the base view header, it's declare the scroller as

touchgfx::DrawableListItems<MenuItem, 5> MenuScrollWheelListItems;

GMeur
Associate III
November 15, 2021

You have to do it like that:

touchgfx::DrawableListItems<ScrollListClickableElement <MenuItem>, 5> MenuScrollWheelListItems;

But you've to write this code in the view, not in the base view (because that file gets overwritten each time you generate your code through the designer). And I fear that in order to do that, you can't create your scrollList using the designer… You're going to have to create it entirely through code…

Tnguy.3
Tnguy.3AuthorBest answer
Associate II
November 15, 2021

I found a working solution base on the example projects.

In my view's hpp file,

class MenuView : public MenuViewBase
{
public:
 MenuView();
 virtual ~MenuView() {}
 virtual void setupScreen();
 virtual void tearDownScreen();
 virtual void handleDragEvent(const DragEvent& evt); // Need to override this to set our flag (below)
 ...
protected:
private:
 bool isClicked = true; // need a flag to differentiate clicks and drags
 
 // Need to declare a callback for item selection (who knew these existed???)
 touchgfx::Callback<MenuView, touchgfx::DrawableListItemsInterface*, int16_t, int16_t> updateItemCallback;
 void updateItemCallbackHandler(touchgfx::DrawableListItemsInterface* items, int16_t containerIndex, int16_t itemIndex);
 ...
};

and then in my cpp:

MenuView::MenuView():
 scrollListItemSelectedCallback(this, &MenuView::scrollListItemSelectedHandler),
 // Connect our item select callback to the handler
 ...
{
 ...
}
 
void MenuView::handleDragEvent(const DragEvent& evt)
{
 if (evt.getDeltaY() != 0 || evt.getDeltaX() != 0)
 {
 // Check to see if the user moved X or Y since touch was detected, if not then it's a click
 isClicked = false;
 }
 else
 {
 isClicked = true;
 }
 MenuViewBase::handleDragEvent(evt); // IMPORTANT: call back to the base so that it handles the drag/scroll
}
 
void MenuView::scrollListItemSelectedHandler(int16_t itemSelected)
{
 // We only want it to register the "Select" if the condition met.
 if (isClicked && itemSelected >= 0 && itemSelected < MenuScrollWheelListItems.getNumberOfDrawables())
 {
 touchgfx::Drawable* d = MenuScrollWheelListItems.getDrawable(itemSelected);
 MenuItem* cc = (MenuItem*)d;
 // Since the itemSelected return the wrong index we needed, we'll get the correct index from our custom public getItemIndex function and update our cc item to match it (it's weird, IK).
 *cc = MenuScrollWheelListItems.element[cc->getItemIndex()];
 cc->onItemClicked(); // a public function in the item (this case it's for MenuItem container) class
 }
 else
 isClicked = true; // reset our flag
}

Hope this helps anyone in the future

GMeur
Associate III
November 23, 2021

Sorry to answer so late. Yeah, that solution works perfectly fine. It only has the inconvenient that whenever you need to implement that logic in a view, you need to override the handleDrag function and that scrollListItemSelectedHandler()… Note that my solution also has the advantage of working with all kind of buttons (buttons that accept long press, repeat,…)

You told me that ScrollListClickableElement is giving you an error saying that ScrollListClickableElement is unknown but did you include the class definition that I showed you above in your project and in the header of your view ?!

Tnguy.3
Tnguy.3Author
Associate II
November 29, 2021

Sorry for the late reply, I was on break. After reviewing your code again I believe that was something I missed. Sorry

sjd.1
Visitor II
March 11, 2022

I had Pyton V2.7 hooked up from some other assignment, and this became blocking the V3.7 Python to put in efficiently by trying on kohli click test.

After uninstalling all and reinstall V3.7.9, I changed into capable of run the pip installation of the wanted modules.