VST Plugin Still Not Getting Keystrokes

This is an old but apparently still unsolved problem. In certain VST Hosts – e.g. Cubase on Windows – Juce labels and text editors do not receive keystrokes because the keystrokes are intercepted by the host sequencer. With some other dev toolkits – e.g. VSTGUI – this is not a problem. This implies that there is a solution that could be incorporated in Juce. This has been discussed in various threads in this forum for a number of years but apparently the problem persists. What is the status?

there was some kind of solution that was mentioned on the closed thread, but i am not sure if it works or not, that had to do with adding the Cubase key strokes to the vst wrapper. did anyone find a defect with this solution?

The Cubase keywords are easily changed (added to or deleted) by the Cubase user so adding a list in the VST Wrapper would not be a very robust solution. I believe the keyboard focus issue needs to be resolved at the OS level. It already works on Mac OSX and VSTGUI is able to capture focus on Windows so there must be a solution.

Yeah, this kept us busy for several work days to solve. This is the solution we came up with, do not know if this is the optimal solution, but it works for us. Also thanks a lot for all the posts in http://www.rawmaterialsoftware.com/viewtopic.php?f=8&t=1662&start=30

Some extra code for Sonar, as Sonar passes keystrokes to the plugins.

First of all, in your , we added this:

  // Request keyboard focus here, so that text fields do not get them automatically.
  setWantsKeyboardFocus(true);

Our <ModalTextEditor.h>:

#if _WIN32
class ModalTextEditor: public juce::TextEditor, public juce::TextEditorListener
{

public:
	explicit ModalTextEditor();

	virtual ~ModalTextEditor();
 
public:
	void setTextColour
	( const juce::Colour& colour );
 
	void setFocusedTextColour
	( const juce::Colour& colour );
	
	virtual void focusGained
	( juce::Component::FocusChangeType cause );

protected:
	void updateTextColour();
	
	virtual void focusLost
	( juce::Component::FocusChangeType cause );

	//-- TextEditorListener
	void textEditorTextChanged
	( TextEditor& editor ) { };

	void textEditorReturnKeyPressed
	( TextEditor& editor );
	
	void textEditorEscapeKeyPressed
	( TextEditor& editor );
	
	void textEditorFocusLost
	( TextEditor& editor ) { };

private:
	juce::Colour m_textColour;
	juce::Colour m_focusedTextColour;
	juce::Component* m_oldParent;
	juce::Rectangle<int> m_oldBounds;

	bool m_isSonar;

	juce::String m_textBeforeEdit;
};

//-----------------------------------------------------------------------------------------
#else

class ModalTextEditor: public juce::TextEditor, public juce::TextEditorListener
{
public:
	ModalTextEditor();
	
	virtual ~ModalTextEditor();

public:
	void setTextColour
	( const juce::Colour& colour ) { };
	
	void setFocusedTextColour
	( const juce::Colour& colour ) { };

protected:
	void mouseDown
	( const juce::MouseEvent& e );

	//-- TextEditorListener-Methoden
	void textEditorTextChanged
	( juce::TextEditor& editor ) { };
	
	void textEditorReturnKeyPressed
	( juce::TextEditor& editor );

	void textEditorEscapeKeyPressed
	( juce::TextEditor& editor );
	
	void textEditorFocusLost
	( juce::TextEditor& editor ) { };

private:
	juce::String m_textBeforeEdit;
};

#endif

Our <ModalTextEditor.cpp>:

#if _WIN32
//==============================================================================
// Windows
//==============================================================================
ModalTextEditor::ModalTextEditor()
: juce::TextEditor("ModalTextEditor")
, m_oldParent(0)
, m_isSonar(false)
, m_textBeforeEdit("")
{
	setPopupMenuEnabled(false);
	setOpaque(true);
	addListener(this);

	// Set <m_isSonar>.
	const juce::String hostPath(juce::File::getSpecialLocation(juce::File::hostApplicationPath).getFullPathName());
	const juce::String hostFilename(juce::File(hostPath).getFileName());
	m_isSonar = hostFilename.containsIgnoreCase("SONAR");
};

//------------------------------------------------------------------------------
ModalTextEditor::~ModalTextEditor()
{
};

//------------------------------------------------------------------------------
void ModalTextEditor::focusGained
( juce::Component::FocusChangeType cause )
{
	if(m_isSonar)
	{
		juce::TextEditor::focusGained(cause);
	}
	else
	{
		if(!isOnDesktop())
		{
			m_oldBounds = getBounds();
			m_oldParent = getParentComponent();
			m_textBeforeEdit = getText();
			addToDesktop(juce::ComponentPeer::windowIsTemporary);
			updateTextColour();
			grabKeyboardFocus();
		}
	
		repaint();
	}
};

//------------------------------------------------------------------------------
void ModalTextEditor::focusLost
( juce::Component::FocusChangeType cause )
{
	if(!m_isSonar)
	{
		if(isOnDesktop() && m_oldParent != 0)
		{
			m_oldParent->addChildComponent(this);
			updateTextColour();
			setBounds(m_oldBounds);
			setHighlightedRegion(juce::Range<int>(0, 0));
		}
		
		repaint();
	}

	if(isCurrentlyModal())
	{
		exitModalState(0);
	}

	juce::TextEditor::focusLost(cause);
};

//------------------------------------------------------------------------------
void ModalTextEditor::textEditorReturnKeyPressed
( juce::TextEditor& editor )
{
	focusLost(focusChangedDirectly);
	moveKeyboardFocusToSibling(true);
	repaint();
};

//------------------------------------------------------------------------------
void ModalTextEditor::textEditorEscapeKeyPressed
( juce::TextEditor& editor )
{
	editor.setText(m_textBeforeEdit);

	focusLost(focusChangedDirectly);
	moveKeyboardFocusToSibling(true);
	repaint();
};

#else
//==============================================================================
// MAC
//==============================================================================
ModalTextEditor::ModalTextEditor()
: juce::TextEditor("ModalTextEditor")
, m_textBeforeEdit("")
{
	addListener(this);
};
	
//------------------------------------------------------------------------------
ModalTextEditor::~ModalTextEditor()
{
};
	
//------------------------------------------------------------------------------
void ModalTextEditor::mouseDown
( const juce::MouseEvent& e )
{
	juce::TextEditor::mouseDown(e);
	m_textBeforeEdit = getText();
	grabKeyboardFocus();
};
	
//------------------------------------------------------------------------------
void ModalTextEditor::textEditorReturnKeyPressed
( juce::TextEditor& editor )
{
	moveKeyboardFocusToSibling(true);
};
	
//------------------------------------------------------------------------------
void ModalTextEditor::textEditorEscapeKeyPressed
( juce::TextEditor& editor )
{
	moveKeyboardFocusToSibling(true);
	editor.setText(m_textBeforeEdit);
};
	
//------------------------------------------------------------------------------
#endif

Thanks so much for this. I will test it ASAP. I’m hoping that if this approach works then Jules will include it in the main release. There are a number of classes that either inherit from or aggregate TextEditor – e.g. Label and Slider via Label – so it becomes kind of a mess to reintegrate these kind of changes on every new Juce release.

to avoid subclassing, we placed all the stuff required for keyboard focus in a combined MouseListener/TextEditorListener.

When creating the PluginEditor, the host type is checked, and the listeners are added to all TextEditors in the component hierarchy. What I like about that approach is that you don’t change the functionality if the same components are used in a standalone application. What I don’t like is that you need to manually add it for all components created after initialization of the PluginEditor (unless they are in a modal dialog which seems to work fine).

Here’s our implementation of such a listener. There’s an ugly work around because textEditorFocusLost was called twice (at least in ableton live on win) and for some reason the editor couldn’t be removed from the desktop in the first call.

    ///////////////////////////////////////////////////////////////////////////
    class KeyboardFocusWorkaroundListener : public MouseListener,
                                            public TextEditorListener
    {
    public:

        KeyboardFocusWorkaroundListener(TextEditor* pTextEditor)
            : m_pTextEditor(pTextEditor),
              m_bRemoveWhenLosingFocus(false),
              m_pOriginalParent(0),
              m_bIsOnDesktop(false)
        {

        }

        void mouseDown(const MouseEvent& e)
        {   
            addEditorToDesktop();
        }

        void mouseExit(const MouseEvent e)
        {
 
        }

        virtual void textEditorTextChanged (TextEditor& editor)       { }
        virtual void textEditorReturnKeyPressed (TextEditor& editor)
        { 
            removeEditorFromDesktop();
        }
        virtual void textEditorEscapeKeyPressed (TextEditor& editor)
        { 
            removeEditorFromDesktop();
        }
        virtual void textEditorFocusLost (TextEditor& editor) 
        { 
            if (m_bRemoveWhenLosingFocus)
            {
                removeEditorFromDesktop();
            }
            else
            {
                m_bRemoveWhenLosingFocus = true;
            }
        }

        juce_UseDebuggingNewOperator

    protected:

        void addEditorToDesktop()
        {
            if (m_bIsOnDesktop) return;

            m_originalBounds = m_pTextEditor->getBounds();
            m_pOriginalParent = m_pTextEditor->getParentComponent();
            
            m_bRemoveWhenLosingFocus = false;

            m_pTextEditor->addToDesktop(ComponentPeer::windowIsTemporary);
            m_pTextEditor->grabKeyboardFocus();

            m_bIsOnDesktop = true;
        }

        void removeEditorFromDesktop()
        {
            if (!m_bIsOnDesktop) return;

            m_pTextEditor->removeFromDesktop();

            if (m_pOriginalParent != 0)
            {
                m_pOriginalParent->addChildComponent(m_pTextEditor);
            }

            m_pTextEditor->setBounds(m_originalBounds);

            m_bIsOnDesktop = false;
        }

    private:

        bool m_bIsOnDesktop;
        bool m_bRemoveWhenLosingFocus;

        Rectangle<int> m_originalBounds;
        Component* m_pOriginalParent;

        TextEditor* m_pTextEditor;

    };

If you’re worried about subclassing, it might be worth going this way (but Jakob’s is probably better tested, we didn’t test much with Sonar). If you come up with something that combines both approaches, I’d be happy to test it in ableton live on win/osx.

Hi all,
Based on the code zamrate wrote few years ago (see an older thread about this problem), I wrote a class called MySliderLabel that I use to edit the value of my slider.
TheText Editor is actually a modal window so it REALLY gets the focus on the keyboard and loses it as soon as the user presses enter, escape or clicks elsewhere.
edit() is called from the ShowTextBox function of my slider.
Hope this will help some of you.
Cheers
Raph

(with a little update of the code)

MySliderLabel.h:

#ifndef __MYSLIDERLABEL_HEADER__
#define __MYSLIDERLABEL_HEADER__

#include "MySlider.h"
class MySlider;

class MySliderTextEditor : public TextEditor
{
public:

// if a mouse down event happens, exit the modal text editor window
	bool canModalEventBeSentToComponent (const Component* comp)
	{
		if (comp->isMouseButtonDownAnywhere())
		{
			exitModalState(0);
		}

		return false;
	}
};



class MySliderLabel : public Label
{
public:
   MySliderLabel();
   ~MySliderLabel();

   void setText(const String &text)
   {
      mText = text;
      repaint();
   }

   const String& getText(){return mText;}
   void edit();
   bool isBeingEdited(){return mbLabelIsEdited;};

  
private:   
	String		mText;
	MySlider*	       mpSlider;
	bool			mbLabelIsEdited;

    void  textEditorReturnKeyPressed (TextEditor &editor)
   {
      editor.exitModalState(0);
   }

    void  textEditorEscapeKeyPressed (TextEditor &editor)
   {
      editor.exitModalState(0);
   }

    void  textEditorFocusLost (TextEditor &editor)
   {
      editor.exitModalState(0);
   }

   void mouseUp (const MouseEvent& e);

};

#endif[/code]



MySliderLabel.cpp:
[code]
// MySliderLabel gets the focus on the sliderLabel

#include "MySliderLabel.h"

MySliderLabel :: MySliderLabel(Slider* slider)
{
  setRepaintsOnMouseActivity(true);	
   mpSlider = (MySlider*) slider;
   mpSlider->setSliderLabelPtr(this);

  mbLabelIsEdited = false;
}

MySliderLabel :: ~MySliderLabel()
{
}

// Caledl from MySlider::ShowTextEditor: pops up a modal text editor 
void MySliderLabel :: edit()
{
	mbLabelIsEdited = true;

	MySliderTextEditor* ed = new MySliderTextEditor();

	ed->setColour(TextEditor::backgroundColourId,Colours::white);
	ed->setColour(TextEditor::textColourId,Colours::black);

	ed->setColour(TextEditor::highlightColourId,Colours::black);
	ed->setColour(TextEditor::highlightedTextColourId,Colours::white);

	ed->setBounds(0, 0, getWidth(), getHeight());
	ed->setFont(Font ("Futura", 11, Font::bold));

	ed->setText(getText(),false);
	Range<int>  range(0, getText().length()); 
	ed->setHighlightedRegion(range);

	addAndMakeVisible(ed);
	ed->addListener(this);

	ed->runModalLoop();

	setText(ed->getText());

	deleteAndZero(ed);

	mbLabelIsEdited = false;
}

void MySliderLabel :: mouseUp (const MouseEvent& e)
{ 
	if (e.mouseWasClicked()
     && contains (e.getPosition())
     && ! e.mods.isPopupMenu()) 
	{
		mpSlider->showTextBox();
	}
}

[/code]

MyLabel.cpp file: 
[code]
#include "MyLookAndFeel.h"
#include "MySlider.h"

//==============================================================================
MySlider:: MySlider(const String& componentName)
               : Slider        (componentName)
{

	mpSliderLabel = NULL;
	setLookAndFeel (MyLookAndFeel::getInstance());
	setBufferedToImage(true);

}
//==============================================================================
MySlider:: ~MySlider()
{
}

//==============================================================================
void MySlider::showTextBox()
{
    if (mpSliderLabel)
      {
		const String suffix = getTextValueSuffix();
		mpSliderLabel->setText(getTextFromValue(getValue()).removeCharacters(suffix));
		mpSliderLabel->edit(); 
		setValue(getValueFromText(mpSliderLabel->getText()));	  
	}
}
[/code]

MySlider.h:
[code]

#ifndef __MY_SLIDER_HEADER__
#define __MY_SLIDER_HEADER__

#include "../JuceLibraryCode/JuceHeader.h"
#include "MySliderLabel.h"

class MySliderLabel;
class MySlider:	public Slider
{
public:
	MySlider(const String& componentName);
	~MySlider();

	void showTextBox();

	void setSliderLabelPtr(MySliderLabel* ptr) {mpSliderLabel = ptr;};

private: 
	MySliderLabel* mpSliderLabel;
};

#endif
[/code]

And filnally the lookAndFeel file:
MyLookAndFeel.h:
[code]#ifndef _MY_LOOKANDFEEL_HEADER_
#define _MY_LOOKANDFEEL_HEADER_

#include "../JuceLibraryCode/JuceHeader.h"
// ==============================================================================

class MyLookAndFeel: public LookAndFeel
{
public:
	static MyLookAndFeel* getInstance();

private:
	Label* createSliderTextBox (Slider& slider); 
};

#endif

MyLookAndFeel.hcpp

[code]
#include “MyLookAndFeel.h”
#include “MySliderLabel.h”

//----------------------------------------------------------------------//
MyLookAndFeel* MyLookAndFeel::getInstance()
{
static MyLookAndFeel myLookAndFeel;
return &myLookAndFeel;
}

//
//==============================================================================

Label* MyLookAndFeel::createSliderTextBox(Slider& slider)
{
MySliderLabel* l = new MySliderLabel(&slider);

l->setJustificationType (Justification::centred);

l->setColour (Label::textColourId, slider.findColour (Slider::textBoxTextColourId));

l->setColour (Label::backgroundColourId,
              (slider.getSliderStyle() == Slider::LinearBar) ? Colours::transparentBlack
                                                             : slider.findColour (Slider::textBoxBackgroundColourId));
l->setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId));

l->setColour (TextEditor::textColourId, slider.findColour (Slider::textBoxTextColourId));

l->setColour (TextEditor::backgroundColourId,
              slider.findColour (Slider::textBoxBackgroundColourId)
                    .withAlpha (slider.getSliderStyle() == Slider::LinearBar ? 0.7f : 1.0f));

l->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId));

return l;

}[/code]

I can’t find ShowTextBox in Label class.Did you write it on your own?

It’s rather unbelievable that this question comes up ever again, when I’ve posted a solution, which worked, but for mysterious reasons never made its way into JUCE. Link: http://www.rawmaterialsoftware.com/viewtopic.php?f=8&t=1662&hilit=keystrokes&start=60 Check out the bottom of page 4 (last code I posted).

Hi Zamrate.

I really like your solution cause it’s not intrusive like other solutions suggested here (like adding components to the desktop upon focus gain).
Though I can’t get the control modifier working in your solution, so I can’t use it for now.

What I did was to add the entire component of my application to the desktop on focus gain/ mouse enter.

I just tried your patch, and keyboard input indeed works perfectly in Live 8.2.1, but there is still trouble in Cubase 6 (32bit).

Many keys work well (numbers, letters, space, backspace, cursors, shift) but others don’t: commas and the period key and the whole number block (which is weird because it looks like its keys are explicitly mapped in your code).

I’d like to get rid of the focus/desktop workarounds, but being able to enter decimal separators is an absolute must for those text fields.

Any ideas wants going wrong here? Maybe it makes a difference that I am using a German keyboard layout?

Dunno, isn’t Cubase maybe just blocking these keys?

I just stepped into it, and there seem to be two differences between Cubase 6 and Live 8 in the calls to your convertVstKeyInputToKeyCodeAndTextCharacter

  • For the keys on the keypad, in Live the ‘index’ is != 0 so they are treated as ascii. In Cubase 6 however, ‘index’ is 0 and ‘value’ equals of the VKEY_ constants instead - no matter if num-lock is active or not. Because ‘textCharacter’ is never set in that case, the TextEditor won’t treat it as text input later. A possible workaround (if you don’t need cursor movement with the keypad) seems to be to additionally set textCharacter to the character that corresponds to the key.

  • the situation for the regular ±., keys is worse. In Cubase 6, you do get an effEditKeyDown event, bot both ‘index’ and ‘value’ are 0. So there doesn’t seem to be chance at all at distinguishing these keys.

HI all,

200gaga:

ShowTextBox is in the Slider class and is called to create and make visible the textbox.

Shlomi :

I did a little improvement of the zamrate patch in juce_VST_wrapper.cpp to make modifier keys work in Cubase. Check it out:

void convertVstKeyInputToKeyCodeAndTextCharacter(int opCode, int value, int index, int &keyCode, int &textCharacter, int &modifierKey)
{
	keyCode=0;
	textCharacter=0;
	modifierKey = 0;


   if (opCode==effEditKeyDown)
   {

		if (value == VKEY_CONTROL || value == VKEY_SHIFT || value == VKEY_ALT) 
		{
			modifierKey = value;
			return;
		}

       for (int i=0; i<sizeof(vstVirtualKeyCodeConversionTable)/(2*sizeof(int));i++)
       {
            if (value==vstVirtualKeyCodeConversionTable[i*2])
            {
               keyCode=vstVirtualKeyCodeConversionTable[i*2+1];
               break;
            }
       }       
      
      textCharacter=index; // ascii
   }
}

and in the dispatcher:

[code] if (opCode == effEditKeyDown || opCode == effEditKeyUp)
{

#if JUCE_WIN32
if (editorComp!=0)
{
int keyCode, textCharacter;
int modifierKey;
convertVstKeyInputToKeyCodeAndTextCharacter(opCode, value, index, keyCode, textCharacter, modifierKey);

        ComponentPeer *peer=editorComp->getPeer();
        if (peer!=0)
		{
			if (keyCode!=0 || textCharacter!=0)
		   {    
			   return peer->handleKeyPress (keyCode, textCharacter); 
			}

			if (modifierKey)
			{
				int keyMods = 0;

				if (modifierKey == VKEY_CONTROL)	keyMods |= ModifierKeys::ctrlModifier;
				if (modifierKey == VKEY_SHIFT)		keyMods |= ModifierKeys::shiftModifier;
				if (modifierKey == VKEY_ALT)		keyMods |= ModifierKeys::altModifier;

				ModifierKeys::getCurrentModifiers().withFlags (keyMods);
			}
		}
        return 0;
     }

#endif
[/code]

I use both methods in my code, the first one is just text boxes, whereas I use the second one for overall keyboard shortcuts, and I’m happy with it.

The solution posted by RiphRaph is the best way to solve this. I have a slightly more basic version which is called a PopupTextEditor, which I think really should be integrated into the main TextEditor code since this comes up over and over again. It would be much more preferable for plugins that they just set a property on the slider text editor saying it should popup in a new window, then vanish again once enter / escape / lost focus. You can even specialise what is below to pull the formatting directly from the label / text editor of the slider.

	class PopupTextEditor;

	class PopupTextEditorListener
	{
	public:
		//==============================================================================
		juce_UseDebuggingNewOperator

		virtual ~PopupTextEditorListener () {};
		virtual void textEditAccepted (PopupTextEditor* editor, const juce::String& text) = 0;
		virtual void textEditRejected (PopupTextEditor* editor) = 0;
	};

	class PopupTextEditor
		: public TextEditorListener
        , public ComponentListener
	{
	public:
		//==============================================================================
		juce_UseDebuggingNewOperator

        class TextEditorModal : public TextEditor
        {
        public:
            TextEditorModal (const juce::String name) : TextEditor (name) {};
            virtual void inputAttemptWhenModal ()
            {
                returnPressed();
                exitModalState (0);
            }
        };
        
		PopupTextEditor (const juce::String& name, PopupTextEditorListener* listener);
		virtual ~PopupTextEditor ();

		const juce::String& getName () const;
		void setColour (const int colourId, const Colour& colour);
		void setFont (const Font& font);
		void showOverWithText (Component* anchor, juce::String& text);

		virtual void textEditorTextChanged (TextEditor& editor);
		virtual void textEditorReturnKeyPressed (TextEditor& editor);
		virtual void textEditorEscapeKeyPressed (TextEditor& editor);
		virtual void textEditorFocusLost (TextEditor& editor);

	protected:
		juce::String name;
        juce::String text;
		PopupTextEditorListener* listener;
        TextEditorModal* texteditor;
	};


PopupTextEditor::PopupTextEditor (const String& name_, PopupTextEditorListener* listener_)
: name (name_)
, listener (listener_)
{
	jassert (listener);
	texteditor = new TextEditorModal (name_);
	texteditor->addListener (this);
	jassert (texteditor);
}

PopupTextEditor::~PopupTextEditor ()
{
	if (texteditor)
	{
		delete texteditor;
	}
}
const String& PopupTextEditor::getName () const
{
	return name;
}
void PopupTextEditor::setColour (const int colourId, const Colour& colour)
{
	texteditor->setColour (colourId, colour);
}
void PopupTextEditor::setFont (const Font& font)
{
	texteditor->setFont (font);
}
void PopupTextEditor::showOverWithText (Component* anchor, String& text)
{
    ModalComponentManager::Callback* userCallback = 0;
    ScopedPointer<ModalComponentManager::Callback> userCallbackDeleter (userCallback);

    Component::SafePointer<Component> prevFocused (Component::getCurrentlyFocusedComponent());
    Component::SafePointer<Component> prevTopLevel ((prevFocused != 0) ? prevFocused->getTopLevelComponent() : 0);
    texteditor->setWantsKeyboardFocus (true);
	Rectangle<int> sr (anchor->getBounds ());
	sr.setPosition (anchor->getScreenX(), anchor->getScreenY());
    int fontheight = texteditor->getFont().getHeight() + 4;
    if (sr.getHeight() > fontheight)
    {
        sr.translate (0, (sr.getHeight() - fontheight)/2);
        sr.setHeight (fontheight);
    }
	texteditor->setAlwaysOnTop (true);
	texteditor->setBounds (sr);
	texteditor->setText (text);
	texteditor->setHighlightedRegion (Range <int> (0, text.length ()));
	texteditor->setVisible (true);
    texteditor->grabKeyboardFocus();

    texteditor->addToDesktop (ComponentPeer::windowIsTemporary, 0);
    
	texteditor->addListener (this);
    texteditor->enterModalState (false);
    texteditor->grabKeyboardFocus ();

    const int result = texteditor->runModalLoop();

    {
        if (prevTopLevel != 0)
            prevTopLevel->toFront (true);

        if (prevFocused != 0)
            prevFocused->grabKeyboardFocus();
    }
}
void PopupTextEditor::textEditorTextChanged (TextEditor& editor)
{
	// do nothing until the return or escape key is pressed
}
void PopupTextEditor::textEditorReturnKeyPressed (TextEditor& editor)
{
	jassert (listener);
	String text = editor.getText ();
	listener->textEditAccepted (this, text);
    texteditor->exitModalState(1);
}
void PopupTextEditor::textEditorEscapeKeyPressed (TextEditor& editor)
{
	jassert (listener);
	listener->textEditRejected (this);
    texteditor->exitModalState(0);
}
void PopupTextEditor::textEditorFocusLost (TextEditor& editor)
{
    texteditor->exitModalState(0);
}

The next issue you are going to have then is tracking the mouse as well as passing keys back to the host.

The Options.txt is not a user friendly solution.

And I think that the modifier if in RiphRaph’s solution:

            if (modifierKey)
            {
               int keyMods = 0;
   
               if (modifierKey == VKEY_CONTROL)   keyMods |= ModifierKeys::ctrlModifier;
               if (modifierKey == VKEY_SHIFT)      keyMods |= ModifierKeys::shiftModifier;
               if (modifierKey == VKEY_ALT)      keyMods |= ModifierKeys::altModifier;

               ModifierKeys::getCurrentModifiers().withFlags (keyMods);
            }

doesn’t really do anything, it needs to be called before the peer->handleKeyPress() with something like updateCurrentModifiers() or as a parameter to handleKeyPress().

Unfortunately I don’t know any complete solution to the keystroke problem yet.

I have to say that this thread confuses me a bit. We have run into the same problem and need a solution, but I am not at all sure what the best solution is based on the previous comments. Here are what we require:

  • We provide text editors (with sliders and a custom xy pad) in our GUI that allows the user to enter floating-point numbers (including the ‘.’ of course).
  • It should work with Cubase and Ableton Live (or any other host for that matter)
  • It should not depend on the user changing host options (at least not in text files)

We are currently working with Juce 1.53. Does any of the aforementioned solutions work for us? Has juce release 2.0 solved this problem?

The only reliable way to get keyboard input is to pop up your own text editor in a new window and then make it go away on a loss of focus.

Hmmm. In other words solutions like the one you presented earlier in this thread, and not solutions that depend on hacking the vst wrapper (such as those presented by zamrate or RiphRaph)?