cancel
Showing results for 
Search instead for 
Did you mean: 

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

Michael K
Senior III

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.
Embedded UI/UX Consulting: cadenza.design
1 ACCEPTED SOLUTION

Accepted Solutions
Alexandre RENOUX
Principal

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

View solution in original post

4 REPLIES 4
Alexandre RENOUX
Principal

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

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

Hello @Alexandre RENOUX (ST Employee),

Thank you for your share. :thumbs_up:

I used your 'SwipeContainerButton' function and the effect is as expected except for one small detail:

Drag by holding down the button it will trigger a button event before turning the page, it's confusing to use. The problem may go down to 'button.handleClickEvent()' functions, it seems that the problem cannot be solved without rewriting this function.

Do you have any professional advice?

Alexandre RENOUX
Principal

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