cancel
Showing results for 
Search instead for 
Did you mean: 

Button with Label and Icon, Hide Icon when Not Pressed

taylors
Associate II

Hello community! Going to say thanks in advance for your help and direction here. Before I get started, understand that I am using an external rotary encoder knob as the user input for the display, no touchscreen. I am hijacking the button pressed/released behavior to show the navigation with the encoder and manually handling the encoder press to load a new screen or other behavior as needed. I have the external hardware mapped in properly and can view the knob turn/press events as needed, no problems there.

I'm trying to build in a menu list of sorts with each menu entry as flex button with some custom behavior. I want for each entry to have an icon, a text label, and a different pressed/released background image, but I only want to have the icon showing when the menu entry is selected. When the entry is not selected, the background image will match the overall UI background to blend in. See the image below where the first entry is selected.

Sub Menus.png

This same menu entry behavior will ultimately live in several screens and I would also like to not have duplicate copies of the code living in each ScreenView file but I'm unsure of where to put these functions to share to all screens while also handling the screen specific menu entry objects. 

The issues I'm trying to solve:

  1. How to hide the icon when the entry is not selected
  2. How to shift the text to the right when the entry is selected and the icon is showing
  3. How best to organize code to have this all live in one place instead of duplicated in all necessary screen files.

I appreciate your help and suggestions.

1 ACCEPTED SOLUTION

Accepted Solutions
JTP1
Lead

Hello

I think you could use Flex button to get everything you need. First set toggle as trigger. Then you can use forceState function to set it pressed or released, like

 

 

 

flexButton1.forceState(0);
//or
flexButton1.forceState(1);

 

 

 

And then use 3 visual elements, Text, Icon, and Image. Add your text,icon and button image to these.

Then, about hiding the icon when button is not pressed. One trick to hide another icon is setting too big index to it, like 0xffff

 

 

 

 

flexButton1.setIconBitmaps(0xffff,Bitmap(BITMAP_ICON_THEME_IMAGES_ACTION_DONE_50_50_0A303D_SVG_ID));
   

 

 

 

 

Then icon will disappear when button is set deactive.  If you open your viewBase.cpp, you will find there the original setIconBitmaps line.

First you can set the same icon to your flexButton -> icon ->  Released and Pressed image selection.

Then, after code generation, copy setIconBitmaps line from viewBase.cpp to your view.cpp setupScreen function and set 0xffff as first parameter. Copy also setIconXY line since set Icons will also move icons to the middle of button.

EDIT: Just realize that you can use same trick to the image to get it hide when it button not selected(pressed). Then you dont have worry about how the possible blended edges would be visible from  background. So copy also setBitmaps- line from viewBase.cpp  to viev.cpp setup- function and replace first value with 0xffff.

About moving text, just simply call setTextPosition when set button pressed or released. For example

 

 

 

flexButton1.setTextPosition(-40, 36, 278, 91);
// or
flexButton1.setTextPosition(0, 36, 278, 91);
// and then 
flexButton1.invalidate();

 

 

 

to move text (first parameter 0 / -40). Once again, copy this line from your viewBase.cpp.

About the overall construction,maybe simplest way is to create customContainer and place one complete menu structure (5 buttons) to this container and then make it work properly. Then it is possible to make copies of this ready menuContainer and for example set corrent texts for each button in every menuContainer with TGFX designer.

Btw are you handling the encoder signals in model.cpp and then bring those to screens thru presenter-class or where you handle the encoder movements ? Just thinking to make good example about this type UI.

Hope this helps, ask more if needed

Br JTP

View solution in original post

7 REPLIES 7
JTP1
Lead

Hello

I think you could use Flex button to get everything you need. First set toggle as trigger. Then you can use forceState function to set it pressed or released, like

 

 

 

flexButton1.forceState(0);
//or
flexButton1.forceState(1);

 

 

 

And then use 3 visual elements, Text, Icon, and Image. Add your text,icon and button image to these.

Then, about hiding the icon when button is not pressed. One trick to hide another icon is setting too big index to it, like 0xffff

 

 

 

 

flexButton1.setIconBitmaps(0xffff,Bitmap(BITMAP_ICON_THEME_IMAGES_ACTION_DONE_50_50_0A303D_SVG_ID));
   

 

 

 

 

Then icon will disappear when button is set deactive.  If you open your viewBase.cpp, you will find there the original setIconBitmaps line.

First you can set the same icon to your flexButton -> icon ->  Released and Pressed image selection.

Then, after code generation, copy setIconBitmaps line from viewBase.cpp to your view.cpp setupScreen function and set 0xffff as first parameter. Copy also setIconXY line since set Icons will also move icons to the middle of button.

EDIT: Just realize that you can use same trick to the image to get it hide when it button not selected(pressed). Then you dont have worry about how the possible blended edges would be visible from  background. So copy also setBitmaps- line from viewBase.cpp  to viev.cpp setup- function and replace first value with 0xffff.

About moving text, just simply call setTextPosition when set button pressed or released. For example

 

 

 

flexButton1.setTextPosition(-40, 36, 278, 91);
// or
flexButton1.setTextPosition(0, 36, 278, 91);
// and then 
flexButton1.invalidate();

 

 

 

to move text (first parameter 0 / -40). Once again, copy this line from your viewBase.cpp.

About the overall construction,maybe simplest way is to create customContainer and place one complete menu structure (5 buttons) to this container and then make it work properly. Then it is possible to make copies of this ready menuContainer and for example set corrent texts for each button in every menuContainer with TGFX designer.

Btw are you handling the encoder signals in model.cpp and then bring those to screens thru presenter-class or where you handle the encoder movements ? Just thinking to make good example about this type UI.

Hope this helps, ask more if needed

Br JTP

LouisB
ST Employee

Hello @taylors ,
 

@JTP1 's solution for your issue seems pretty complete, you should give it a try ;).

Louis BOUDO
ST Software Developer | TouchGFX

Thanks for this! I will check it out and post back the results.

@JTP1Thanks for your suggestion! This is working perfectly! I'm a little concerned by setting the bitmap inactive bitmap index to 0xFFFF (or just forcing it to a non-valid image) because I would think passing an invalid parameter could eventually be worked in as a scenario to watch for and bail early if the image doesn't exist. It would be awesome if there were a reserved value for a non-valid image, or allowing for one of the images to be non-defined at the start. For background and icons.

I am having an issue with redrawing the icons after updating the pressed state. My turn handler is working and updating the entry index value but not redrawing the flex buttons in their new state, even after calling the invalidate function.

 

flexButton.invalidate()

 

For you final question about the external encoder handling, in the Model::tick() I built in a simple handler that is reading an osMessageQueue holding custom event enum objects as they come in through an interrupt handler which will call a pressHandler() or turnHandler() function specific for the presenter screen as needed.

From ModelListener.hpp:

 

class ModelListener
{
public:
    ModelListener() : model(0) {}
    
    virtual void knobPress(bool isPressed) {}
    virtual void knobTurn(int8_t turnDirection) {}

    virtual ~ModelListener() {}

    void bind(Model* m)
    {
        model = m;
    }
protected:
    Model* model;
};

 

And from Model.cpp:

 

void Model::tick()
{
	uint32_t pendingEventCount = osMessageQueueGetCount(_ec11EventQueueHandle);
	while(pendingEventCount)
	{
		pendingEventCount--;

		Ec11Event_t pendingEvent;
		osStatus_t status = osMessageQueueGet(_ec11EventQueueHandle, &pendingEvent, 0, 0);
		if (status != osOK)
			continue;

		if (!modelListener)
			break;

		switch (pendingEvent)
		{
			case Event_ButtonPress:
				modelListener->knobPress(true);
				break;

			case Event_ButtonRelease:
				modelListener->knobPress(false);
				break;

			case Event_KeyLeft:
				modelListener->knobTurn(-1);
				break;

			case Event_KeyRight:
				modelListener->knobTurn(1);
				break;

			default:
				break;
		}
	}
}

 

For each screen I have implemented the turnHandler to deselect one button and press the next for a given list, etc. 

Hope that helps. Thanks again!

 - Taylor

JTP1
Lead

Hello Taylor

Good to hear you get forward.

Maybe I was bit too hurry, after rechecking this is right way to mark other bitmap invalid

 

	flexButton1.setIconBitmaps(Bitmap(BITMAP_INVALID),Bitmap(BITMAP_ICON_THEME_IMAGES_ACTION_DONE_50_50_0A303D_SVG_ID));

 

This INVALID_BITMAP is defined in Bitmap.hpp which is included from BitmapDatabase.hpp.

This INVALID_BITMAP seems to be used also in source code of container and widgets so I assume this way its safe.

You're absolutely right about need to disable icon or image for example in button pressed or released state directly in the Desinger  @GaetanGodart

I attach my test code, it works perfectcly when change state pressed of released (test button handlers), is there some principled difference to yours since you have problems in icon updating ?

About the model class, it looks good. Thank you for sharing it.  Using the osMessageQueue for transferring the control events is right way and clean implementation.

All the best

Br JTP

Awesome. Thanks for the invalid bitmap value. I'll add that in.

I was able to fix the issue with the button invalidate function not working (bug in my code, passing the button to a function by value instead of by reference). No problems here anymore.

Also, thanks for the suggestion with the containers. I was able to build up a "menu" container object that is replicated across 4 different screens very quickly now with minimal repeated code. 

Thanks again,

 - Taylor 

JTP1
Lead

Very good. Note to add also this Bitmap- function call with invalid bitmap- parameter, it was missing in my initial post. This function call and the pointer it returns makes the actual difference, the defined invalid index value is the same 0xffff.

Nice to hear that the idea of container and making copies it worked. I think one of the biggest challenges in TGFX documention is lack of 'best practices', how to setup widgets and how to join them together in custom containers to get good functional and reusable blocks.

Br JTP