Can't set keyboard focus


#1

I’ve had a problem with ‘popup’ components I want to give keyboard focus. I’ve tryed different approaches using grabKeyboardFocus, setWantsKeyboardFocus, moveKeyboardFocusToSibling etc with no luck.

You here have a simple application illustrating my problem. I can’t write in my textEditor until I have clicked the app with the mouse. Can someone give me an advice?

[code]/* ==============================================================================

  • Demonstrating problems keyboard focus in JUCE
  • ==============================================================================
    */

#include “includes.h”

class MainComponent : public Component
{
public:
//==============================================================================
MainComponent () : focusLabel (0), textEditor(0) {
addAndMakeVisible (focusLabel = new Label (String::empty,T(“Write your name:”)));
addAndMakeVisible (textEditor = new TextEditor(String::empty));
textEditor->setTextToShowWhenEmpty(T(""),Colours::black);
textEditor->setWantsKeyboardFocus(true);
textEditor->grabKeyboardFocus();
setSize (600, 300);
}

~MainComponent() {
    deleteAndZero (focusLabel);
}

void paint (Graphics& g){}
void resized() {
	focusLabel->setBounds (152, 80, 296, 48);
	textEditor->setBounds (152, 120,296, 48);
}
juce_UseDebuggingNewOperator

private:
Label* focusLabel;
TextEditor* textEditor;

MainComponent (const MainComponent&);
const MainComponent& operator= (const MainComponent&);

};

class FocusTestWindow : public DocumentWindow
{
public:
//==============================================================================
FocusTestWindow()
: DocumentWindow (T(“Focus Test”),
Colours::lightgrey,
DocumentWindow::allButtons,
true)
{
MainComponent* const contentComponent = new MainComponent();
setContentComponent (contentComponent, true, true);
centreWithSize (getWidth(), getHeight());
setVisible (true);
}

~FocusTestWindow() {}

//==============================================================================
void closeButtonPressed()
{
    JUCEApplication::quit();
}

};

class FocusTestApplication : public JUCEApplication
{
FocusTestWindow* focusTestWindow;

public:
FocusTestApplication() : focusTestWindow (0) {}
~FocusTestApplication() {}

void initialise (const String& commandLine)
{
	focusTestWindow = new FocusTestWindow();
}

void shutdown()
{
    if (focusTestWindow != 0) delete focusTestWindow;
}

//==============================================================================
const String getApplicationName()    {return T("Focus Test"); }
const String getApplicationVersion() {return T("1.0"); }
bool moreThanOneInstanceAllowed()    {return true; }
void anotherInstanceStarted (const String& commandLine) {}

};

START_JUCE_APPLICATION (FocusTestApplication)
[/code]


#2

It’s probably because you’re making all these calls before the component is actually on the screen. Until it’s visible inside a visible window, there’s no way it can give it focus. Try getting your window fully created and on-screen, and then grabbing focus.


#3

Oh - it is that simple. I did actually try to put the grab into resized() without success, but in paint() it works. Thanx.

Is paint() the right place to put it or can you think of some better place?

It seems like my app doesn’t always want to release focus again. Probably because it now takes it on every paint().

How do you other folks handle this?


#4

did you try overriding Component::visibilityChanged () and put it there?


#5

Well I’ve tryed to override visibilityChanged() but it seems to be too early.

The best result I get by adding the following to paint()

void paint(Graphics& g){
        DBG(T("Paint"));
      	if (!focusGiven && textEditor->isVisible())
    	{
    		DBG(T("Focus given"));
    		textEditor->grabKeyboardFocus();
    		focusGiven=true;
    	}

Basically it makes no difference whether isVisible() is checked or not.

Having a DBG in beginning of VisibilityChanged() I get the following output while starting my app and “alt-tab”-ing between this app and other apps (or doing the same by using the mouse:

VisibilityChanged
Paint
Focus given
Paint
Paint
Paint
Paint

I’ll try this approach in my ‘real’ app


#6

Overriding paint() function works perfectly, thank you for the suggestion. That's how I override my paint() function in MainContentComponent file:

 

void MainContentComponent::paint(Graphics& g)

{
  if (!this->hasKeyboardFocus(true) && this->isVisible())
  {
    this->grabKeyboardFocus();
  }
}

#7

Or use a static...

    void paint(Graphics& g) override {
        static bool firstTime = true;
        if (firstTime) {
            grabKeyboardFocus();
            firstTime = false;
        }
    }

This avoids cluttering the namespace with an extra flag.

π


#8

..to any beginners reading this: NOOOOOOOOO!!! Avoid statics in general, and this is a particularly bad way to use them!


#9

Use of static in this case will fail miserably when you have two or more components.

Do not use statics!


#10

If you grab focus in:

	void visibilityChanged() override
	{
		if (isVisible())
		{
			grabKeyboardFocus();
		}
	}

You’ll get an assert for isShowing() in the focus code for some reason.


#11

As mentioned above, you can only grab focus if your component is showing on the screen. This means that not only this component needs to be visible, but also all ancestors (parent, parent of a parent etc) up to the top level have to be visible. Then, the top level component’s peer has to be “not minimised”, i.e. your window must be not minimised.

More robust solution is to use ComponentMovementWatcher which will call for you componentVisibilityChanged when isShowing() state may have potentially changed. Simply override its callback as follows:

void componentVisibilityChanged() override
{
    if (isShowing())
        grabKeyboardFocus();
}

Grabbing focus in paint() call is usually not ideal, because:

  • grabbing focus often means redrawing element losing/taking focus (thus another repaint)
  • paint() should paint, and not do any logic like grabbing focus :slight_smile: