Skip to main content
Michael K
Senior III
December 15, 2020
Solved

Workaround for swipe containers not swiping if dragged on a button within

  • December 15, 2020
  • 2 replies
  • 3433 views

I have a swipe container with multiple pages. On each page there are a few buttons (children of their respective pages). Swiping in empty areas within the container swipes the container left and right, as intended. However, if I swipe left and right on top of the buttons, the button is pressed in and no container swiping occurs.

I understand that click events are passed to the topmost visible element. However, is there a way to detect if a drag has occurred/is occurring and if so, pass that event to the swipe container instead of the button?

Thanks!

N.B.:

  • The same issue occurs with scrolling containers.
  • This issue occurs with any ClickListener, not just buttons.
  • The desired behavior is modeled after most smartphone menus where a drag after a press will cancel the button click and scroll the window instead.
This topic has been closed for replies.
Best answer by Alexandre RENOUX

Hello,

To do so, you need to manually decide which event should be triggered. When you click, are the coordinates matching the button area ? If yes, then raise a flag buttonAreaClicked = true for instance. Then you call each eventHandler depending on this flag.

I created a small example you will find enclosed. However there's one issue. If while doing your swipe, the button is still in pressed mode and you release your click, it will trigger the button and the swipe container at the same time. One solution would be to create your own button and AbstractButton class (just copy paste the existing ones in the /gui/common/ folders) and add a function that would publicly set the boolean value pressed from the ScreenView. This way, if you detect a drag, you can set the button to pressed = false and fix this issue.

Please note that by creating your own button class, you will not be able to use the Designer anymore for buttons of your custom class (this is why I didn't do it in this example).

There is maybe a better way but that is my current idea.

When your question is answered, please close this topic by choosing Select as Best.

/Alexandre

2 replies

Alexandre RENOUX
Alexandre RENOUXBest answer
Visitor II
December 16, 2020

Hello,

To do so, you need to manually decide which event should be triggered. When you click, are the coordinates matching the button area ? If yes, then raise a flag buttonAreaClicked = true for instance. Then you call each eventHandler depending on this flag.

I created a small example you will find enclosed. However there's one issue. If while doing your swipe, the button is still in pressed mode and you release your click, it will trigger the button and the swipe container at the same time. One solution would be to create your own button and AbstractButton class (just copy paste the existing ones in the /gui/common/ folders) and add a function that would publicly set the boolean value pressed from the ScreenView. This way, if you detect a drag, you can set the button to pressed = false and fix this issue.

Please note that by creating your own button class, you will not be able to use the Designer anymore for buttons of your custom class (this is why I didn't do it in this example).

There is maybe a better way but that is my current idea.

When your question is answered, please close this topic by choosing Select as Best.

/Alexandre

Michael K
Michael KAuthor
Senior III
December 17, 2020

Thanks for your comment. The code example you provided was the perfect starting point I needed to fit the solution to my application. Indeed I had issues with the button being triggered after the swipe, but I managed to fix that without creating any new classes. I basically keep a state machine of whether the button is released, clicked or is being dragged. Most importantly, if the state moves from Clicked to Dragged, I create a fake CANCEL clickevent that I pass along to the ViewBase, while passing the real clickevent to just the swipe container. See edited code below.

enum class ClickStatus {
	RELEASED,
	CLICKED,
	CLICK_DRAGGING
};
 
ClickStatus s = ClickStatus::RELEASED;
 
 
void ScreenView::handleClickEvent(const ClickEvent& evt)
{
	const int numClickables = 3;
	Drawable *clickableItems[numClickables] = { &btn_1, &btn_2, &btn_3 };
	
	bool clickIntersect = false;
	for (int i = 0; i < numClickables; i++) {
		if (clickableItems[i]->getAbsoluteRect().intersect(evt.getX(), evt.getY())) {
			clickIntersect = true;
			break;
		}
	}
 
	if (clickIntersect)
	{
		// Click Event occurred within a button
		if (evt.getType() == ClickEvent::PRESSED)
		{
			if (evt.getType() == ClickEvent::PRESSED) {
				if (s != ClickStatus::CLICK_DRAGGING) {
					// If we aren't dragging, allow press event to propagate to button
					s = ClickStatus::CLICKED;
					ScreenViewBase::handleClickEvent(evt);
				}
			}
		}
		else if (evt.getType() == ClickEvent::RELEASED) {
			if (s == ClickStatus::CLICK_DRAGGING) {
				// Dragging now, send release event only to the swipe container
				swipeContainer1.handleClickEvent(evt);
			}
			else {
				// Not dragging, allow passing of the event to buttons
				ScreenViewBase::handleClickEvent(evt);
			}
			s = ClickStatus::RELEASED;
		}
	}
	else {
		// Click is not on one of the buttons, act normally
		ScreenViewBase::handleClickEvent(evt);
	}
}
 
 
 
void ScreenView::handleDragEvent(const DragEvent& evt)
{
	const int numClickables = 3;
	Drawable *clickableItems[numClickables] = { &btn_1, &btn_d2, &btn_3 };
 
	bool dragIntersect = false;
	for (int i = 0; i < numClickables; i++) {
		if (clickableItems[i]->getAbsoluteRect().intersect(evt.getOldX(), evt.getOldY())) {
			dragIntersect = true;
			break;
		}
	}
	
	if (dragIntersect)
	{
		// Drag occurred within a button
		if (abs(evt.getDeltaX()) > 2 && s == ClickStatus::CLICKED)
		{
			// We are now dragging, cancel any button press by creating a new click event 
 // and forwarding it to the rest of the view
			ClickEvent e2(ClickEvent::CANCEL, evt.getOldX(), evt.getOldY(), 1);
			ScreenViewBase::handleClickEvent(e2);
			s = ClickStatus::CLICK_DRAGGING;
		}
	}
 
	if (s == ClickStatus::CLICK_DRAGGING) {
		// We are dragging, forward drag events to swipe container only
		swipeContainer1.handleDragEvent(evt);
	}
	else {
		// Not dragging, allow drags to pass through
		ScreenViewBase::handleDragEvent(evt);
	}
}
 
void ScreenView::handleGestureEvent(const GestureEvent& evt)
{
	if (s == ClickStatus::CLICK_DRAGGING) {
		// We are dragging, forward gesture events to swipe container only
		swipeContainer1.handleGestureEvent(evt);
	}
	else {
		// Not dragging, allow gestures to pass through
		ScreenViewBase::handleGestureEvent(evt);
	}
}
Embedded UI/UX Consulting: cadenza.design
Alexandre RENOUX
Visitor II
November 8, 2021

Hello V.H

Actually what @Michael K​ suggested is a very nice way of fixing the issue.

Creating a fake CANCEL clickevent that you pass to the ViewBase is very appropriate in this case.

For ease, please find enclosed the project updated with the fix from Michael K. I also updated the project to TouchGFX 4.18.

Enjoy ;)

/Alexandre