cancel
Showing results for 
Search instead for 
Did you mean: 

Button long press and custom widget over code generation question

Thomas Laurenson
Associate II

Hi,

For my application I need a transparent (invisible) button with long press action.

I found this topic https://touchgfx.zendesk.com/hc/en-us/community/posts/206420309-Button-Longpress

It was helpful in fact, but I still have some issues.

I implemented this new widget in code.

Then for testing purpose I created a small project with 2 screens, and 1 button on each screen to swap between screens ( I did this from TouchGFX)

Then back to cube IDE, I modified Screen1ViewBase like this:

class Screen1ViewBase : public touchgfx::View<Screen1Presenter>
{
public:
    Screen1ViewBase();
    virtual ~Screen1ViewBase() {}
 
    virtual void setupScreen();
 
protected:
    FrontendApplication& application() {
        return *static_cast<FrontendApplication*>(Application::getInstance());
    }
 
    /*
     * Member Declarations
     */
    touchgfx::Box box1;
   // touchgfx::Button button1;
    touchgfx:: HoldableButton button1;   //-> redefinition of button1 to a HoldableButton
 
private:
 
    /*
     * Callback Handler Declarations
     */
    void buttonCallbackHandler(const touchgfx::AbstractButton& src);
 
    /*
     * Callback Declarations
     */
    touchgfx::Callback<Screen1ViewBase, const touchgfx::AbstractButton&> buttonCallback;
 
};

Note that I only changed the definition of button1 from Button to HoldableButton.

The good news is that the long press does work !

But my problems are:

1/ Is there a way to call this new custom widget from TouchGFX and not only from code ?

because if I have to go back to TouchGFX, all my code is erased as Screen1ViewBase .hpp and .cpp are generated files. Is there a way to prevent that, ? or maybe I should call the widget elsewhere ?

2/ How to make this button invisible from code ?

3/ last but not least: the long press works, but the action is also triggered when the button is released and I do not want that, if the button is released before the long press occurs, it should do nothing.

The implementation proposed in the linked topic overwrites the 'handleClickEvent' method. But when the button is released before the long press ' AbstractButton::handleClickEvent ' get called (seen with a breakpoint).

Why does this happen ? Can i prevent that ?

Thanks a lot :).

Thomas.

1 ACCEPTED SOLUTION

Accepted Solutions
Martin KJELDSEN
Chief III

1/ No, there's not. Anything in user code cannot be seen or modifed by the designer outside of renaming files (due to screen renaming).

2/ myButton.setVisible(false);

3/ I think i need to see your Button code, but it most likely inherits from some standard button which will always fire an event on release. But you have the code for AbstractButton which standard click/release buttons inherit from, so you could just create a new class and inherit from that instead or copy the code from AbstractButton into your own custom button class.

Here's the code for AbstractButton. I've commented the part of handleClickEvent that we do not want to execute in a long press.

#include <touchgfx/widgets/AbstractButton.hpp>
 
namespace touchgfx
{
void AbstractButton::handleClickEvent(const ClickEvent& event)
{
    bool wasPressed = pressed;
    pressed = (event.getType() == ClickEvent::PRESSED);
    if ((pressed && !wasPressed) || (!pressed && wasPressed))
    {
        // Pressed state changed, so invalidate
        invalidate();
    }
#if 0 // NEVER handle released event
    if (wasPressed && (event.getType() == ClickEvent::RELEASED) && action)
    {
        // This is a click. Fire callback.
        if (action->isValid())
        {
            action->execute(*this);
        }
    }
#endif
}
} // namespace touchgfx

View solution in original post

8 REPLIES 8
Martin KJELDSEN
Chief III

1/ No, there's not. Anything in user code cannot be seen or modifed by the designer outside of renaming files (due to screen renaming).

2/ myButton.setVisible(false);

3/ I think i need to see your Button code, but it most likely inherits from some standard button which will always fire an event on release. But you have the code for AbstractButton which standard click/release buttons inherit from, so you could just create a new class and inherit from that instead or copy the code from AbstractButton into your own custom button class.

Here's the code for AbstractButton. I've commented the part of handleClickEvent that we do not want to execute in a long press.

#include <touchgfx/widgets/AbstractButton.hpp>
 
namespace touchgfx
{
void AbstractButton::handleClickEvent(const ClickEvent& event)
{
    bool wasPressed = pressed;
    pressed = (event.getType() == ClickEvent::PRESSED);
    if ((pressed && !wasPressed) || (!pressed && wasPressed))
    {
        // Pressed state changed, so invalidate
        invalidate();
    }
#if 0 // NEVER handle released event
    if (wasPressed && (event.getType() == ClickEvent::RELEASED) && action)
    {
        // This is a click. Fire callback.
        if (action->isValid())
        {
            action->execute(*this);
        }
    }
#endif
}
} // namespace touchgfx

Thomas Laurenson
Associate II

Hi Martin,

Thanks for the tips.

In fact the implementation from https://touchgfx.zendesk.com/hc/en-us/community/posts/206420309-Button-Longpress, that I was using, inherited from classic Button.

As you suggested I created an HoldableButton class that inherite directly from AbstractButton instead, and it's now working as intended :).

HoldableButton.hpp:

#ifndef HOLDABLEBUTTON_HPP
#define HOLDABLEBUTTON_HPP
 
#include <touchgfx/widgets/AbstractButton.hpp>
#include <touchgfx/Bitmap.hpp>
 
namespace touchgfx
{
/**
 * @class Button Button.hpp touchgfx/widgets/Button.hpp
 *
 * @brief A button with two states.
 *
 *        A button consists of two images, one for its normal state and one when it is pressed
 *        down.
 *
 * @see AbstractButton
 */
class HoldableButton : public AbstractButton
{
public:
 
    /**
     * @fn Button::Button()
     *
     * @brief Default constructor.
     *
     *        Default constructor.
     */
	HoldableButton() : AbstractButton(), up(), down(), alpha(255) { }
 
    /**
     * @fn virtual Button::~Button()
     *
     * @brief Destructor.
     *
     *        Destructor.
     */
    virtual ~HoldableButton() { }
 
    /**
     * @fn virtual void Button::draw(const Rect& invalidatedArea) const;
     *
     * @brief Draws the given invalidated area.
     *
     * @param invalidatedArea The rectangle to draw, with coordinates relative to this drawable.
     *
     * @see Drawable::draw()
     */
    virtual void draw(const Rect& invalidatedArea) const;
 
    /**
     * @fn virtual void Button::setBitmaps(const Bitmap& bmpReleased, const Bitmap& bmpPressed);
     *
     * @brief Sets the bitmaps used by this button.
     *
     *        Sets the bitmaps used by this button.
     *
     * @param bmpReleased Bitmap to use when button is released.
     * @param bmpPressed  Bitmap to use when button is pressed.
     */
    virtual void setBitmaps(const Bitmap& bmpReleased, const Bitmap& bmpPressed);
 
    /**
     * @fn virtual Rect Button::getSolidRect() const;
     *
     * @brief Gets solid rectangle.
     *
     *        Gets solid rectangle.
     *
     * @return largest possible solid rect. Delegated to the largest solid rect of the button
     *         bitmap(s).
     */
    virtual Rect getSolidRect() const;
 
    /**
     * @fn void Button::setAlpha(uint8_t alpha)
     *
     * @brief Sets the alpha value for the image.
     *
     *        Sets the alpha value for the image.
     *
     * @param alpha The alpha value. 255 = completely solid.
     */
    void setAlpha(uint8_t alpha)
    {
        this->alpha = alpha;
    }
 
    /**
     * @fn uint8_t Button::getAlpha() const
     *
     * @brief Gets the current alpha value.
     *
     *        Gets the current alpha value.
     *
     * @return The current alpha value.
     */
    uint8_t getAlpha() const
    {
        return alpha;
    }
 
    /**
     * @fn Bitmap Button::getCurrentlyDisplayedBitmap() const
     *
     * @brief Gets currently displayed bitmap.
     *
     *        Function to obtain the currently displayed bitmap, which depends on the button's
     *        pressed state.
     *
     * @return The bitmap currently displayed.
     */
    Bitmap getCurrentlyDisplayedBitmap() const
    {
        return (AbstractButton::pressed ? down : up);
    }
 
    /**
     * @fn virtual uint16_t Button::getType() const
     *
     * @brief For GUI testing only.
     *
     *        For GUI testing only. Returns type of this drawable.
     *
     * @return TYPE_BUTTON.
     */
    virtual uint16_t getType() const
    {
        return (uint16_t)TYPE_BUTTON;
    }
 
    void handleClickEvent(const touchgfx::ClickEvent& event);
 
 
    void handleTickEvent();
 
protected:
    Bitmap  up;    ///< The image to display when button is released.
    Bitmap  down;  ///< The image to display when button is pressed.
    uint8_t alpha; ///< The current alpha value. 255 denotes solid, 0 denotes completely transparent.
 
private:
#ifdef SIMULATOR
	static const int startTicksBeforeContinuous = 10;
#else
	static const int startTicksBeforeContinuous =100;
#endif
	int ticksBeforeContinuous;
	int ticks;
 
};
} // namespace touchgfx
 
#endif // HOLDABLEBUTTON_HPP

Holdablebutton.cpp

#include <touchgfx/widgets/HoldableButton.hpp>
 
namespace touchgfx
{
void HoldableButton::draw(const Rect& invalidatedArea) const
{
    Bitmap bmp(AbstractButton::pressed ? down : up);
    Rect dirty(0, 0, bmp.getWidth(), bmp.getHeight());
    dirty &= invalidatedArea;
    if ((bmp.getId() != BITMAP_INVALID) && !dirty.isEmpty())
    {
        Rect r;
        translateRectToAbsolute(r);
        HAL::lcd().drawPartialBitmap(bmp, r.x, r.y, dirty, alpha);
    }
}
 
void HoldableButton::setBitmaps(const Bitmap& bmpReleased, const Bitmap& bmpPressed)
{
    up = bmpReleased;
    down = bmpPressed;
    // Adjust width and height of this widget to match bitmap. It is assumed
    // that the two bitmaps have same dimensions.
    Drawable::setWidth(down.getWidth());
    Drawable::setHeight(down.getHeight());
}
 
Rect HoldableButton::getSolidRect() const
{
    if (alpha < 255)
    {
        return Rect(0, 0, 0, 0);
    }
 
    return (AbstractButton::pressed ? down.getSolidRect() : up.getSolidRect());
}
 
 void HoldableButton::handleClickEvent(const touchgfx::ClickEvent& event)
{
	    bool wasPressed = pressed;
	    pressed = (event.getType() == ClickEvent::PRESSED);
	    if ((pressed && !wasPressed) || (!pressed && wasPressed))
	    {
	        // Pressed state changed, so invalidate
	        invalidate();
	    }
	    if(pressed)
	    {
	    	ticks = 0;
	    	ticksBeforeContinuous = startTicksBeforeContinuous;
	    	touchgfx::Application::getInstance()->registerTimerWidget(this);
	    }
	    else if (wasPressed && (event.getType() == ClickEvent::RELEASED))
	    {
	    	ticks = 0;
	    	touchgfx::Application::getInstance()->unregisterTimerWidget(this);
	    }
}
 
 void HoldableButton::handleTickEvent()
{
	  AbstractButton::handleTickEvent();
	 if(pressed)
	 {
		 if(ticks == ticksBeforeContinuous)
		 {
			 if(action && action->isValid())
			 {
				 action->execute(*this);
			 }
 
			 ticks = 0;
			 if(ticksBeforeContinuous)
			 {
				 ticksBeforeContinuous--;
			 }
		 }
		 else
		 {
			 ticks++;
		 }
	 }
}
} // namespace touchgfx
 

Martin KJELDSEN
Chief III

Amazing. Good work @Thomas Laurenson​ 🙂

/Martin

Martin KJELDSEN
Chief III

Next is creating your own..

AbstractHoldableButton

KAkao.1
Associate

Hello,

I am total new to STM microcontrollers and i am up to reach the same goal as @Thomas Laurenson​ did assuming a year ago 🙂 I am using F429I discovery board and

what i am trying to do is simply creating a push button on the LCD screen. The buttons of the current widgets don' t allow me to reach the goal. Because they execute in release state. I have generated my code in TouchGFX and set my GPIOs trough CUBEMX and defined my functions in CUBE IDE so far.

However, my goal is to have the LCD panel executed my functions as long as the button is pressed and not execute when user releases the button. I' am not a well skilled programmer yet but still tried to implement the .hpp and .cpp files as HoldableButton class in to the project folder include/widgets and source/widgets. Edited the ...viewScreenBase. Then whenever i tried to build the project i am having multiple errors.

Can anyone guide me on how to implement holdable widget feature to the IDE properly. Any help would be a lot appreciated.

Thanks,

Kakaoo

​Hi, please search this forum. I've helped with this "issue" before, how to create a button that fires on "press" and not on "release" (standard)

KAkao.1
Associate

Hi, thanks for the advice but i searched and couldn't find anything i can use :(

​Allow me to point you in the right direction then.

A Button in TouchGFX inherits from AbstractButton (which fires on release). Create your own class that defines handleClickEvent() and look very closely at the lines where the callback is fired =) "//This is a click. Fire callback."

#include <touchgfx/widgets/AbstractButton.hpp>
 
namespace touchgfx
{
void AbstractButton::handleClickEvent(const ClickEvent& event)
{
    bool wasPressed = pressed;
    pressed = (event.getType() == ClickEvent::PRESSED);
    if ((pressed && !wasPressed) || (!pressed && wasPressed))
    {
        // Pressed state changed, so invalidate
        invalidate();
    }
    if (wasPressed && (event.getType() == ClickEvent::RELEASED) && action)
    {
        // This is a click. Fire callback.
        if (action->isValid())
        {
            action->execute(*this);
        }
    }
}
} // namespace touchgfx