Closed mxa closed 1 year ago
@mxa , can you provide a little bit more information, which kind of project (standalone, vst3), which host, etc... Thank you
Hello @scheffle,
We build a VST3, the issue was tested with Bitwig Studio 4.3.10 on Linux.
I subclassed CAnimKnob
and implemented the mouse functions as follows:
namespace VSTGUI {
CKickButtonHover::CKickButtonHover(const VSTGUI::CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, CBitmap* triggerBitmap, const CPoint& offset)
: CAnimKnob(size, listener, tag, background, offset)
{}
CMouseEventResult CKickButtonHover::onMouseDown (CPoint& where, const CButtonState& buttons)
{
LOG_DEBUG("mouse down on trigger view \n");
if (!(buttons & kLButton))
return kMouseEventNotHandled;
value = 1.0;
if (isDirty ())
{
invalid ();
valueChanged ();
}
beginEdit ();
return onMouseMoved(where, buttons);
}
CMouseEventResult CKickButtonHover::onMouseUp (CPoint& where, const CButtonState& buttons)
{
LOG_DEBUG("mouse up on trigger view \n");
if (!(buttons & kLButton))
return kMouseEventNotHandled;
value = 0.5;
if (isDirty ())
{
invalid ();
valueChanged ();
}
endEdit ();
return kMouseEventHandled;
}
CMouseEventResult CKickButtonHover::onMouseEntered (CPoint& where, const CButtonState& buttons)
{
LOG_DEBUG("mouse entered trigger view \n");
value = 0.5;
if (isDirty ())
{
invalid ();
valueChanged ();
}
return kMouseEventHandled;
}
CMouseEventResult CKickButtonHover::onMouseExited (CPoint& where, const CButtonState& buttons)
{
LOG_DEBUG("mouse exited trigger view \n");
value = 0.0;
if (isDirty ())
{
invalid ();
valueChanged ();
}
return kMouseEventHandled;
}
bool CKickButtonHover::onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float &distance, const CButtonState &buttons)
{
return false;
}
}
I just re-tested after changes to the order of isDirty(), invalid()
and valueChanged()
and the mouse up event is correctly received. But the value of 1.0 of this event somehow doesn't get properly received in the GUI.
Hi @clwe , do you get mouse ups on normal CAnimKnobs?
Sorry, the edit of my comment from yesterday didn't get saved. As stated above:
I just re-tested after changes to the order of isDirty(), invalid() and valueChanged() and the mouse up event is correctly received. But the value of 1.0 of this event somehow doesn't get properly received in the GUI.
The problem might to be somewhere else, not in this part of the code. The strange thing is that this code works perfectly fine on Mac OS and Windows. We implement a hovering function with CAnimknob
with three bitmaps:
value = 0.0 -> button inactive (bitmap 0)
value = 0.5 -> button hover (bitmap 1)
value = 1.0 -> button clicked (bitmap 2)
The strange behaviour on linux is the following:
On first mouse down we see bitmap number 0 - corresponding to value = 0.0
(erratic behavior)
On all succeeding mouse downs we see bitmap number 2 - corresponding to value = 1.0
(expected behavior)
After more testing I found that this problem is not linux related at all - but is host specific: It happens in Reaper and Bitwig, but the problem does not show in Ableton Live. My gut feeling says, it could be something related with the automation recording or state saving capabilities of the host(?)
@clwe , it still is interesting if unmodified CAnimKnobs work in those hosts. Error analysis will be much easier if this is known.
Yes, we use several animation knobs as shipped with the sdk and they work as expected in all the mentioned DAWs (but do not implement hovering by default).
OK, just reread your stuff. You use the CControl::value member in your subclass to select bitmaps. Don't do this, add your own member variable to do this.
OK. How do I inform the host, that my member variable changed?
Why does the host need to know that you have switched bitmaps?
Should your subclass still be used as a knob?
You are right, the host doesn't need to know. But the UI needs to update the bitmap according to my member variable somehow. (I don't know the inner workings of CAnimknob.) It's used as a trigger button actually. It has three states as mentioned above.
Whenever you want to change the bitmap, you call invalid() and set your own class member to the index depending on the situation. Your draw methods will be called a little bit later where you then draw the bitmap according to the index you previously have set in your class member.
Aha, thanks! So I would have to re-implement this function:
void CAnimKnob::draw (CDrawContext *pContext)
{
if (getDrawBackground ())
{
CPoint where (0, 0);
float val = getValueNormalized ();
if (val >= 0.f && heightOfOneImage > 0.)
{
CCoord tmp = heightOfOneImage * (getNumSubPixmaps () - 1);
if (bInverseBitmap)
where.y = floor ((1. - val) * tmp);
else
where.y = floor (val * tmp);
where.y -= (int32_t)where.y % (int32_t)heightOfOneImage;
}
getDrawBackground ()->draw (pContext, getViewSize (), where);
}
setDirty (false);
}
What is the index
you were referring to? How do I add my class to the index?
Also, do I have to implement setDirty()
as well?
Hmm. I changed the code slightly and don't call onMouseMoved()
anymore. I also call beginEdit()
and endEdit()
directly in sequence and it works! The graphics get displayed correctly now.
CMouseEventResult CKickButtonHover::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (!(buttons & kLButton))
return kMouseEventNotHandled;
value = 1.0;
if (isDirty ())
{
invalid ();
valueChanged ();
}
beginEdit ();
endEdit ();
return kMouseEventHandled; // onMouseMoved(where, buttons);
}
CMouseEventResult CKickButtonHover::onMouseUp (CPoint& where, const CButtonState& buttons)
{
if (!(buttons & kLButton))
return kMouseEventNotHandled;
value = 0.5;
if (isDirty ())
{
invalid ();
valueChanged ();
}
return kMouseEventHandled;
}
Is there any side effect that I don't know of?
Well, not exactly. If I press down and drag the mouse, then the bitmap gets stuck in the pressed state (value=1) until you press or exit the hover area. So I guess I will need to go the longer way and re-implement the methods.
Hi, I think it is not a good idea to inherit from a control class which implements a special behavior like the CAnimKnob and implement another behavior into it like a kick button as in your case. I've drafted a version of what you want and how I would implement it:
class CKickButtonHover : public CControl
{
public:
CKickButtonHover (const CRect& r, IControlListener* l, int32_t tag) : CControl (r, l, tag) { std::fill (bitmaps.begin (), bitmaps.end (), nullptr); }
void setBitmaps (CBitmap* inactive, CBitmap* hover, CBitmap* clicked)
{
bitmaps[UIState::Inactive] = inactive;
bitmaps[UIState::Hover] = hover;
bitmaps[UIState::Clicked] = clicked;
}
void draw (CDrawContext* context) override
{
if (auto bitmap = bitmaps[state])
bitmap->draw (context, getViewSize ());
setDirty (false);
}
void onMouseDownEvent (MouseDownEvent& event) override
{
beginEdit ();
setValue (1.);
valueChanged ();
state = UIState::Clicked;
invalid ();
event.consumed = true;
}
void onMouseUpEvent (MouseUpEvent& event) override
{
setValue (0.);
valueChanged ();
endEdit ();
if (getViewSize().pointInside(event.mousePosition))
state = UIState::Hover;
else
state = UIState::Inactive;
invalid ();
event.consumed = true;
}
void onMouseCancelEvent (MouseCancelEvent& event) override
{
if (isEditing ())
endEdit ();
state = UIState::Inactive;
invalid ();
}
void onMouseEnterEvent (MouseEnterEvent& event) override
{
if (isEditing ())
return;
state = UIState::Hover;
invalid ();
}
void onMouseExitEvent (MouseExitEvent& event) override
{
if (isEditing ())
return;
state = UIState::Inactive;
invalid ();
}
CLASS_METHODS_NOCOPY (CKickButtonHover, CControl)
private:
enum UIState
{
Inactive,
Hover,
Clicked,
Count
};
UIState state {UIState::Inactive};
std::array<SharedPointer<CBitmap>, UIState::Count> bitmaps;
};
It would be nice to have usable examples for these basic things. My snarky comment from 2 years ago still holds.
VSTGUI is open source, it lives from the participation of its users. When no one provides "usable examples", then there are no "usable examples". And it's always discussable what "usable" actually means. Can we close this issue now, or are you still have issue with mouse up events?
Thanks a lot for your time and help! I haven't found the time yet to implement it as above, will do ASAP. The issue can be closed - mouse up events are received as advertised!
Our project works on Mac but on Linux the mouse button release is never received resulting in unexpected behaviour of the GUI.