cancel
Showing results for 
Search instead for 
Did you mean: 

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

Tnguy.3
Associate III

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)

1 ACCEPTED SOLUTION

Accepted Solutions
Tnguy.3
Associate III

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

View solution in original post

8 REPLIES 8
GMeur
Senior
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.

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;

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…

Thanks for the answer. When I try to implement the item as ScrollListClickableElement like you showed, It's giving me an error saying that ScrollListClickableElement is unknown. Can you tell me which library to include since I cant seem to find any docs on this.

Tnguy.3
Associate III

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
Senior

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 ?!

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

sjd.1
Associate

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.