I am working with a raspberry pi. I have a rotary encoder with a push button connected. I have code that reads the encoder and button fine.
But I would like to create a popup menu when the button is pressed, then turn the encoder to move around the menu the select when the button is pressed. Creating and shown the menu is easy, but how do I get control of the menu from program input from of encoder?
The problem with that idea is that popup menus work on mouse over, not click. I dont see any mouse classes that do that. Even if I did I would need to know the x/y coordinates of each selection to move the mouse around. If that is how it needs to be done it would be easier to create my own menu dialog using controls that can be selected easily.
I think what @ttg is suggesting is you send fake keypresses to the menu, as once the menu is open up/down/left/right/enter/esc will let you navigate the menus. How you achieve that with a PopupMenu is another question.
PopupMenu does also supports keyPressed (checkout the juce_PopupMenu.cpp).
It seems to be tricky to obtain the actual popup. butā¦
You can implement in your custom LookAndFeel: void preparePopupMenuWindow (Component&) override;
which gives you the current menu you can mock the keypress on.
I thought I knew how to do this but I realized that I donāt know how to generate keyboard input. I tried KeyPressed(key, this) but that calls my keyListener and the menu never gets it.
Someone asked about creating keyboard input programmatically and Jules answered that the only way sending a message on windows message queue. I am using linux and would also like it to work on windows.
So how do I do that for linux (and windows)?
I thought maybe I could call the menu directly with keyPressed() but that method does not exist in PopupMenu.
Iāve made a VERY dirty hacky example code for you.
The worst thing in my example is that I keep a ādanglingā pointer to the popup. (it will crash when it is closed).
But this example works on macOS at least. and youāre not actually calling keyboard. you just āmockā the keyboard callback on the PopupMenu itself.
#pragma once
#include <JuceHeader.h>
//==============================================================================
/*
This component lives inside our window, and this is where you should put all
your controls and content.
*/
class MainComponent : public juce::Component
, juce::Timer
{
public:
//==============================================================================
MainComponent()
{
for (auto i = 0; i < 8; i++)
{
m.addItem ("Item " + juce::String (i), {});
}
setSize (400, 400);
m.setLookAndFeel (&popupCatcher);
m.showMenuAsync (juce::PopupMenu::Options());
startTimerHz (1);
}
~MainComponent() override {}
void timerCallback() override
{
if (popupCatcher.lastMenuWindow != nullptr)
{
popupCatcher.lastMenuWindow->keyPressed (juce::KeyPress (juce::KeyPress::downKey));
}
}
struct PopupCatcher : juce::LookAndFeel_V4
{
void preparePopupMenuWindow (juce::Component& c) override
{
// that's NOT cool. you should make sure you don't keep this dangling!! this is purely for quick and dirty example.
lastMenuWindow = &c;
}
juce::Component* lastMenuWindow {nullptr};
} popupCatcher;
private:
juce::PopupMenu m;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
Iām trying to make this work but Iām not a c++ wizard. I cannot get the struct to compile.
I inserted the struct in a dialog class that contains the popup menu. But I get compile errors (the raspberry pi compiler always points to the wrong lines on the error). I get 2 errors:
ā¦/ā¦/Source/ChannelDlgPM.h:139:2: error: expected class-name before ā{ā token
and
ā¦/ā¦/Source/ChannelDlgPM.h:140:15: error: āvoid ChannelDlgPM::PopupCatcher::preparePopupMenuWindow(juce::Component&)ā marked āoverrideā, but does not override
void preparePopupMenuWindow (juce::Component& c) override
The code:
struct PopupCatcher : juce::LookAndFeel_V4
{ <ā line 139
void preparePopupMenuWindow (juce::Component& c) override <ā line 140
{
lastMenuWindow = &c;
}
juce::Component* lastMenuWindow ;
} popupCatcher ;
Iām on an older release of juce, and LookAndFeel_V4 doesnāt compile. Compiling with V3 compiles. I donāt know why the compiler does complain that LookAndFeel_v4 but now it compiles. Now to see if I can make it work.
Now for the last question. As you noted, I have a pointer to an object that can go away without my knowing it. If someone clicks, the menu goes away. So if I rotate the encode, or press the button it will crash.
Is there a way to catch the destructor of the popup, clear the pointer, then call the destructor?
I replaced lastPopupWIndow with nullptr when the menu is closed. I forgot that it returns 0 if the menu is closed without a selection. Then check for nullptr before using.
This has been working great but I have one issue. When I press the encoder, the menu pops up.
Using the popup catcher idea, I send right arrow to go to a submenu and return to select. This is easy when the popup menu has all submenus, and each supbmenu has only items. The popup catcher sends right arrow on first press, return on the second. But the popup catcher has no idea what menus are currently displayed. So if a menu were open that has both submenus and items, the catcher is unaware and may send the wrong keystroke.
Is there a way check what menu/item is selected when the popup catcher get the command? Alternatively is there a way to make a popup menu either select the item with a right arrow or move to the selected sub menu even if a return is pressed?
(dont see the docs how menus are supposed to respond to keyboard input)