Keyboard shortcuts stop working in TabbedComponent

Hi!

I am using an ApplicationCommandManager to get keyboard shortcuts for switching between tabs in a TabbedComponent, which works fine for JUCE components. However, when I use a basic component generated by the Projucer in a tab the shortcuts don’t work when that tab is active. Simplified code demonstrating the issue below. What am I doing wrong here?

    #pragma once

    #include <JuceHeader.h>

    class TestComponent
    	: public Component
    {
    public:
    	TestComponent() {}
    	~TestComponent() {}

    	void paint(Graphics& g) override
    	{
    		g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId));
    		g.setColour(Colours::white);
    		g.setFont(24.0f);
    		g.drawText("TestComponent", getLocalBounds(), Justification::centred, true);
    	}

    private:
    	JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TestComponent)
    };


    class MainComponent
    	: public Component
    	, public ApplicationCommandTarget
    {
    public:
    	enum KeyPressCommandID
    	{
    		nextTab = 1,
    		previousTab,
    	};


    	MainComponent()
    	{
    		textButton1.setButtonText("I am on tab 1");
    		textButton2.setButtonText("I am on tab 2");


    		// Tabbed component
    		auto& buttonBar = tabbedComponent.getTabbedButtonBar();
    		buttonBar.setColour(TabbedButtonBar::ColourIds::tabTextColourId, Colours::lightgrey);
    		buttonBar.setColour(TabbedButtonBar::ColourIds::frontTextColourId, Colours::white);

    		tabbedComponent.addTab("Button 1", Colours::black, &textButton1, false);
    		tabbedComponent.addTab("Button 2", Colours::black, &textButton2, false);
    		tabbedComponent.addTab("Component", Colours::black, &testComponent, false);

    		addAndMakeVisible(tabbedComponent);


    		// Command manager
    		commandManager.registerAllCommandsForTarget(this);
    		commandManager.setFirstCommandTarget(this);

    		auto tlc = getTopLevelComponent();

    		tlc->addKeyListener(commandManager.getKeyMappings());
    		Timer::callAfterDelay(300, [tlc] { tlc->grabKeyboardFocus(); });


    		setSize(600, 400);
    	}


    	~MainComponent() {}


    	void paint(Graphics& g) override
    	{
    		g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId));
    	}


    	void resized() override
    	{
    		tabbedComponent.setBounds(getBounds());
    	}


    	ApplicationCommandTarget* getNextCommandTarget() override
    	{
    		return nullptr;
    	}


    	void getAllCommands(Array<CommandID>& commands) override
    	{
    		commands.addArray({ 
    			KeyPressCommandID::nextTab,
    			KeyPressCommandID::previousTab, });
    	}
    	

    	void getCommandInfo(CommandID commandID, ApplicationCommandInfo& result) override
    	{
    		switch (commandID)
    		{
    		case KeyPressCommandID::nextTab:
    			result.setInfo("Next tab", "Next tab", "Keyboard shortcut", 0);
    			result.addDefaultKeypress(KeyPress::tabKey,
    				ModifierKeys::ctrlModifier);
    			break;
    		case KeyPressCommandID::previousTab:
    			result.setInfo("Previous tab", "Previous tab", "Keyboard shortcut", 0);
    			result.addDefaultKeypress(KeyPress::tabKey,
    				ModifierKeys::ctrlModifier | ModifierKeys::shiftModifier);
    			break;
    		default:
    			break;
    		};
    	}


    	bool perform(const InvocationInfo& info) override
    	{
    		const auto currentTabIndex{ tabbedComponent.getCurrentTabIndex() };
    		const auto numTabs{ tabbedComponent.getNumTabs() };
    		int newTabIndex{ tabbedComponent.getCurrentTabIndex() };

    		switch (info.commandID)
    		{
    		case KeyPressCommandID::nextTab:
    			newTabIndex++;
    			newTabIndex = (newTabIndex >= numTabs) ? 0 : newTabIndex;
    			tabbedComponent.setCurrentTabIndex(newTabIndex);
    			break;

    		case KeyPressCommandID::previousTab:
    			newTabIndex--;
    			newTabIndex = (newTabIndex < 0) ? numTabs - 1 : newTabIndex;
    			tabbedComponent.setCurrentTabIndex(newTabIndex);
    			break;

    		default:
    			return false;
    			break;
    		}

    		return true;
    	}

    private:
    	ApplicationCommandManager commandManager;
    	TabbedComponent tabbedComponent{ TabbedButtonBar::Orientation::TabsAtTop };
    	TextButton textButton1;
    	TextButton textButton2;
    	TestComponent testComponent;

    	JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent)
    };

By “works fine for JUCE components” I mean built-in components like TextButton. I’ve since discovered that’s not true for all of JUCE’s components. A third party component I’m using works fine, but I can’t tell what’s different in the one’s that work.

Never mind, calling setWantsKeyboardFocus(true); in the constructor of the new component did the trick.