Files
hot-step-cpp-ROCm/engine/vendor/vst3sdk/vstgui4/vstgui/lib/cviewcontainer.cpp
T
2026-08-16 18:24:52 +07:00

1584 lines
43 KiB
C++

// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#include "cviewcontainer.h"
#include "coffscreencontext.h"
#include "cbitmap.h"
#include "cframe.h"
#include "ccolor.h"
#include "ifocusdrawing.h"
#include "itouchevent.h"
#include "viewlayouter/autosizeviewlayouter.h"
#include "iviewlistener.h"
#include "controls/icontrollistener.h"
#include "cgraphicspath.h"
#include "controls/ccontrol.h"
#include "dragging.h"
#include "dispatchlist.h"
#include "events.h"
#include "finally.h"
#include <algorithm>
#include <cassert>
namespace VSTGUI {
IdStringPtr kMsgLooseFocus = "LooseFocus";
const CViewAttributeID kCViewContainerDropTargetAttribute = 'vcdt';
const CViewAttributeID kCViewContainerMouseDownViewAttribute = 'vcmd';
const CViewAttributeID kCViewContainerLastDrawnFocusAttribute = 'vclf';
const CViewAttributeID kCViewContainerBackgroundOffsetAttribute = 'vcbo';
//-----------------------------------------------------------------------------
// CViewContainer Implementation
//-----------------------------------------------------------------------------
struct CViewContainer::Impl
{
using ViewContainerListenerDispatcher = DispatchList<IViewContainerListener*>;
ViewContainerListenerDispatcher viewContainerListeners;
CGraphicsTransform transform;
ViewList children;
SharedPointer<IViewLayouter> layouter {AutoSizeViewLayouter::get ()};
CDrawStyle backgroundColorDrawStyle {kDrawFilledAndStroked};
CColor backgroundColor {kBlackCColor};
};
//------------------------------------------------------------------------
struct CViewContainerDropTarget : public IDropTarget, public NonAtomicReferenceCounted
{
CViewContainerDropTarget (CViewContainer* container) : container (container) {}
CPoint getLocalPos (const CPoint& pos) const
{
auto viewSize = container->getViewSize ();
CPoint where2 (pos);
where2.offset (-viewSize.left, -viewSize.top);
container->getTransform ().inverse ().transform (where2);
return where2;
}
DragOperation onDragEnter (DragEventData data) final
{
assert (dropTarget == nullptr);
return onDragMove (data);
}
DragOperation onDragMove (DragEventData data) final
{
CView* view = container->getViewAt (
data.pos, GetViewOptions ().mouseEnabled ().includeViewContainer ());
data.pos = getLocalPos (data.pos);
if (view == currentDragView)
{
if (dropTarget)
return dropTarget->onDragMove (data);
return DragOperation::None;
}
if (currentDragView)
{
if (dropTarget)
dropTarget->onDragLeave (data);
dropTarget = nullptr;
currentDragView = nullptr;
}
if (view)
{
currentDragView = view;
if ((dropTarget = currentDragView->getDropTarget ()))
{
dropTarget->onDragEnter (data);
return dropTarget->onDragMove (data);
}
}
return DragOperation::None;
}
void onDragLeave (DragEventData data) final
{
if (currentDragView)
{
if (dropTarget)
{
data.pos = getLocalPos (data.pos);
dropTarget->onDragLeave (data);
dropTarget = nullptr;
}
currentDragView = nullptr;
}
}
bool onDrop (DragEventData data) final
{
if (dropTarget)
{
data.pos = getLocalPos (data.pos);
auto result = dropTarget->onDrop (data);
dropTarget = nullptr;
currentDragView = nullptr;
return result;
}
currentDragView = nullptr;
return false;
}
CViewContainer* container;
SharedPointer<IDropTarget> dropTarget;
SharedPointer<CView> currentDragView;
};
//-----------------------------------------------------------------------------
/**
* CViewContainer constructor.
* @param rect the size of the container
*/
CViewContainer::CViewContainer (const CRect &rect)
: CView (rect)
{
pImpl = std::unique_ptr<Impl> (new Impl ());
setAutosizingEnabled (true);
}
//-----------------------------------------------------------------------------
CViewContainer::CViewContainer (const CViewContainer& v)
: CView (v)
{
pImpl = std::unique_ptr<Impl> (new Impl ());
pImpl->transform = v.getTransform ();
pImpl->backgroundColorDrawStyle = v.pImpl->backgroundColorDrawStyle;
pImpl->backgroundColor = v.pImpl->backgroundColor;
setBackgroundOffset (v.getBackgroundOffset ());
for (auto& view : v.pImpl->children)
addView (static_cast<CView*> (view->newCopy ()));
}
//-----------------------------------------------------------------------------
CViewContainer::~CViewContainer () noexcept
{
vstgui_assert (pImpl->children.empty ());
vstgui_assert (pImpl->viewContainerListeners.empty ());
}
//-----------------------------------------------------------------------------
void CViewContainer::beforeDelete ()
{
IDropTarget* dropTarget = nullptr;
if (getAttribute (kCViewContainerDropTargetAttribute, dropTarget))
{
removeAttribute (kCViewContainerDropTargetAttribute);
dropTarget->forget ();
}
// remove all views
CViewContainer::removeAll (true);
CView::beforeDelete ();
}
//-----------------------------------------------------------------------------
void CViewContainer::registerViewContainerListener (IViewContainerListener* listener)
{
pImpl->viewContainerListeners.add (listener);
}
//-----------------------------------------------------------------------------
void CViewContainer::unregisterViewContainerListener (IViewContainerListener* listener)
{
pImpl->viewContainerListeners.remove (listener);
}
//-----------------------------------------------------------------------------
void CViewContainer::parentSizeChanged ()
{
for (const auto& pV : pImpl->children)
pV->parentSizeChanged (); // notify children that the size of the parent or this container has changed
}
//-----------------------------------------------------------------------------
void CViewContainer::setMouseDownView (CView* view)
{
auto mouseDownView = getMouseDownView ();
if (mouseDownView && mouseDownView != view)
{
// make sure the old mouse down view get a mouse cancel or if not implemented a mouse up
if (auto cvc = mouseDownView->asViewContainer ())
cvc->setMouseDownView (nullptr);
else
{
MouseCancelEvent cancelEvent;
mouseDownView->dispatchEvent (cancelEvent);
if (!cancelEvent.consumed)
{
MouseUpEvent upEvent;
upEvent.mousePosition = mouseDownView->getViewSize ().getTopLeft () - CPoint (10, 10);
mouseDownView->dispatchEvent (upEvent);
}
}
}
setAttribute (kCViewContainerMouseDownViewAttribute, view);
}
//-----------------------------------------------------------------------------
CView* CViewContainer::getMouseDownView () const
{
CView* view = nullptr;
if (getAttribute (kCViewContainerMouseDownViewAttribute, view))
return view;
return nullptr;
}
//-----------------------------------------------------------------------------
void CViewContainer::clearMouseDownView ()
{
removeAttribute (kCViewContainerMouseDownViewAttribute);
}
//-----------------------------------------------------------------------------
CRect CViewContainer::getLastDrawnFocus () const
{
CRect r;
if (getAttribute (kCViewContainerLastDrawnFocusAttribute, r))
return r;
return {};
}
//-----------------------------------------------------------------------------
void CViewContainer::setLastDrawnFocus (CRect r)
{
if (r.isEmpty ())
removeAttribute (kCViewContainerLastDrawnFocusAttribute);
else
setAttribute (kCViewContainerLastDrawnFocusAttribute, r);
}
//-----------------------------------------------------------------------------
auto CViewContainer::getChildren () const -> const ViewList&
{
return pImpl->children;
}
//-----------------------------------------------------------------------------
void CViewContainer::setTransform (const CGraphicsTransform& t)
{
if (getTransform () != t)
{
pImpl->transform = t;
pImpl->viewContainerListeners.forEach ([this] (IViewContainerListener* listener) {
listener->viewContainerTransformChanged (this);
});
}
}
//-----------------------------------------------------------------------------
const CGraphicsTransform& CViewContainer::getTransform () const
{
return pImpl->transform;
}
//-----------------------------------------------------------------------------
void CViewContainer::setAutosizingEnabled (bool state)
{
setViewFlag (kAutosizeSubviews, state);
}
//-----------------------------------------------------------------------------
void CViewContainer::setViewLayouter (const SharedPointer<IViewLayouter>& layouter)
{
if (layouter == nullptr)
pImpl->layouter = AutoSizeViewLayouter::get ();
else
pImpl->layouter = layouter;
}
//-----------------------------------------------------------------------------
SharedPointer<IViewLayouter> CViewContainer::getViewLayouter () const { return pImpl->layouter; }
//-----------------------------------------------------------------------------
std::optional<ViewLayout> CViewContainer::calculateViewLayout (const CRect& newSize) const
{
if (pImpl->layouter)
{
return pImpl->layouter->calculateLayout (*this, pImpl->children, newSize);
}
return {};
}
//-----------------------------------------------------------------------------
bool CViewContainer::applyViewLayout (const ViewLayout& layout)
{
bool result = true;
if (pImpl->layouter)
{
setViewFlag (kInApplyLayout, true);
result = pImpl->layouter->applyLayout (*this, pImpl->children, layout);
setViewFlag (kInApplyLayout, false);
}
else
{
setViewSize (layout.size);
setMouseableArea (layout.size);
}
return result;
}
//-----------------------------------------------------------------------------
bool CViewContainer::inApplyViewLayout () const { return hasViewFlag (kInApplyLayout); }
//-----------------------------------------------------------------------------
/**
* @param rect the new size of the container
* @param invalid the views to dirty
*/
void CViewContainer::setViewSize (const CRect &rect, bool invalid)
{
if (rect == getViewSize ())
return;
if (!inApplyViewLayout () && pImpl->layouter)
{
if (auto layout = pImpl->layouter->calculateLayout (*this, pImpl->children, rect))
{
if (applyViewLayout (*layout))
{
parentSizeChanged ();
return;
}
}
}
CView::setViewSize (rect, invalid);
parentSizeChanged ();
}
//-----------------------------------------------------------------------------
/**
* @param rect size to get visible size of
* @return visible size of rect
*/
CRect CViewContainer::getVisibleSize (const CRect& rect) const
{
CRect viewSize (getViewSize ());
getTransform ().inverse ().transform (viewSize);
CRect result (rect);
result.offset (viewSize.left, viewSize.top);
result.bound (viewSize);
if (getFrame () == this)
{}
else if (auto parent = getParentView ())
result = static_cast<CViewContainer*> (parent)->getVisibleSize (result);
result.offset (-viewSize.left, -viewSize.top);
return result;
}
//-----------------------------------------------------------------------------
bool CViewContainer::sizeToFit ()
{
bool treatAsColumn = (getAutosizeFlags () & kAutosizeColumn) != 0;
bool treatAsRow = (getAutosizeFlags () & kAutosizeRow) != 0;
if (treatAsColumn || treatAsRow)
return false;
constexpr auto CoordMax = std::numeric_limits<CCoord>::max ();
constexpr auto CoordMin = -CoordMax;
CRect bounds (CoordMax, CoordMax, CoordMin, CoordMin);
for (const auto& pV : pImpl->children)
{
if (pV->isVisible ())
{
CRect vs (pV->getViewSize ());
if (vs.left < bounds.left)
bounds.left = vs.left;
if (vs.right > bounds.right)
bounds.right = vs.right;
if (vs.top < bounds.top)
bounds.top = vs.top;
if (vs.bottom > bounds.bottom)
bounds.bottom = vs.bottom;
}
}
if (bounds == CRect (CoordMax, CoordMax, CoordMin, CoordMin))
return false;
CRect vs (getViewSize ());
vs.right = vs.left + bounds.right + bounds.left;
vs.bottom = vs.top + bounds.bottom + bounds.top;
setViewSize (vs);
setMouseableArea (vs);
return true;
}
//-----------------------------------------------------------------------------
/**
* @param color the new background color of the container
*/
void CViewContainer::setBackgroundColor (const CColor& color)
{
if (color != pImpl->backgroundColor)
{
pImpl->backgroundColor = color;
setDirty (true);
}
}
//------------------------------------------------------------------------------
CColor CViewContainer::getBackgroundColor () const
{
return pImpl->backgroundColor;
}
//------------------------------------------------------------------------------
void CViewContainer::setBackgroundOffset (const CPoint& p)
{
if (p == CPoint (0, 0))
removeAttribute (kCViewContainerBackgroundOffsetAttribute);
else
setAttribute (kCViewContainerBackgroundOffsetAttribute, p);
}
//------------------------------------------------------------------------------
CPoint CViewContainer::getBackgroundOffset () const
{
CPoint p;
if (getAttribute (kCViewContainerBackgroundOffsetAttribute, p))
return p;
return {};
}
//------------------------------------------------------------------------------
void CViewContainer::setBackgroundColorDrawStyle (CDrawStyle style)
{
if (pImpl->backgroundColorDrawStyle != style)
{
pImpl->backgroundColorDrawStyle = style;
setDirty (true);
}
}
//------------------------------------------------------------------------------
CDrawStyle CViewContainer::getBackgroundColorDrawStyle () const
{
return pImpl->backgroundColorDrawStyle;
}
//------------------------------------------------------------------------------
CMessageResult CViewContainer::notify (CBaseObject* sender, IdStringPtr message)
{
if (message == kMsgNewFocusView)
{
auto* view = dynamic_cast<CView*> (sender);
if (view && isChild (view, false) && getFrame ()->focusDrawingEnabled ())
{
CCoord width = getFrame ()->getFocusWidth ();
CRect viewSize (view->getViewSize ());
viewSize.extend (width, width);
invalidRect (viewSize);
}
}
else if (message == kMsgOldFocusView)
{
auto ldf = getLastDrawnFocus ();
if (!ldf.isEmpty ())
{
invalidRect (ldf);
setLastDrawnFocus (CRect (0, 0, 0, 0));
}
}
return kMessageUnknown;
}
//-----------------------------------------------------------------------------
/**
* @param pView the view object to add to this container
* @param pBefore the view object
* @return true on success. false if view was already attached
*/
bool CViewContainer::addView (CView *pView, CView* pBefore)
{
if (!pView)
return false;
vstgui_assert (!pView->isSubview (), "view is already added to a container view");
if (pBefore)
{
auto it = std::find (pImpl->children.begin (), pImpl->children.end (), pBefore);
vstgui_assert (it != pImpl->children.end ());
pImpl->children.insert (it, pView);
}
else
{
pImpl->children.emplace_back (pView);
}
pView->setSubviewState (true);
pImpl->viewContainerListeners.forEach ([&] (IViewContainerListener* listener) {
listener->viewContainerViewAdded (this, pView);
});
if (isAttached ())
{
pView->attached (this);
pView->invalid ();
}
return true;
}
//-----------------------------------------------------------------------------
/**
* @param pView the view object to add to this container
* @param mouseableArea the view area in where the view will get mouse events
* @param mouseEnabled bool to set if view will get mouse events
* @return true on success. false if view was already attached
*/
bool CViewContainer::addView (CView* pView, const CRect &mouseableArea, bool mouseEnabled)
{
if (addView (pView, nullptr))
{
pView->setMouseEnabled (mouseEnabled);
pView->setMouseableArea (mouseableArea);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
/**
* @param withForget bool to indicate if the view's reference counter should be decreased after removed from the container
* @return true on success
*/
bool CViewContainer::removeAll (bool withForget)
{
clearMouseDownView ();
setInitialFocusView (nullptr);
auto it = pImpl->children.begin ();
while (it != pImpl->children.end ())
{
auto view = *it;
if (isAttached ())
view->removed (this);
pImpl->children.erase (it);
view->setSubviewState (false);
pImpl->viewContainerListeners.forEach ([&] (IViewContainerListener* listener) {
listener->viewContainerViewRemoved (this, view);
});
if (withForget)
view->forget ();
it = pImpl->children.begin ();
}
return true;
}
//-----------------------------------------------------------------------------
/**
* @param pView the view which should be removed from the container
* @param withForget bool to indicate if the view's reference counter should be decreased after removed from the container
* @return true on success
*/
bool CViewContainer::removeView (CView *pView, bool withForget)
{
auto it = std::find (pImpl->children.begin (), pImpl->children.end (), pView);
if (it != pImpl->children.end ())
{
if (pView == getInitialFocusView ())
setInitialFocusView (nullptr);
pView->invalid ();
if (pView == getMouseDownView ())
clearMouseDownView ();
if (isAttached ())
pView->removed (this);
pView->setSubviewState (false);
pImpl->viewContainerListeners.forEach ([&] (IViewContainerListener* listener) {
listener->viewContainerViewRemoved (this, pView);
});
if (withForget)
pView->forget ();
pImpl->children.erase (it);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
/**
* @param pView the view which should be checked if it is a child of this container
* @return true on success
*/
bool CViewContainer::isChild (CView* pView) const
{
return isChild (pView, false);
}
//-----------------------------------------------------------------------------
bool CViewContainer::isChild (CView *pView, bool deep) const
{
bool found = false;
if (deep)
{
auto it = pImpl->children.begin ();
while (!found && it != pImpl->children.end ())
{
CView* v = (*it);
if (pView == v)
{
found = true;
break;
}
if (CViewContainer* container = v->asViewContainer ())
found = container->isChild (pView, true);
++it;
}
}
else
{
found = std::find (pImpl->children.begin (), pImpl->children.end (), pView) != pImpl->children.end ();
}
return found;
}
//-----------------------------------------------------------------------------
bool CViewContainer::hasChildren () const
{
return !pImpl->children.empty ();
}
//-----------------------------------------------------------------------------
/**
* @return number of subviews
*/
uint32_t CViewContainer::getNbViews () const
{
return static_cast<uint32_t> (pImpl->children.size ());
}
//-----------------------------------------------------------------------------
/**
* @param index the index of the view to return
* @return view at index. NULL if view at index does not exist.
*/
CView* CViewContainer::getView (uint32_t index) const
{
auto it = pImpl->children.begin ();
std::advance (it, index);
if (it != pImpl->children.end ())
return *it;
return nullptr;
}
//-----------------------------------------------------------------------------
/**
* @param view view which z order position should be changed
* @param newIndex index of new z position
* @return true if z order of view changed
*/
bool CViewContainer::changeViewZOrder (CView* view, uint32_t newIndex)
{
if (newIndex < getNbViews ())
{
uint32_t oldIndex = 0;
auto src = pImpl->children.begin ();
for (;src != pImpl->children.end () && *src != view; ++src, ++oldIndex);
if (src != pImpl->children.end ())
{
if (newIndex == oldIndex)
return true;
if (newIndex > oldIndex)
++newIndex;
auto dest = pImpl->children.begin ();
std::advance (dest, newIndex);
pImpl->children.insert (dest, view);
pImpl->children.erase (src);
pImpl->viewContainerListeners.forEach ([&] (IViewContainerListener* listener) {
listener->viewContainerViewZOrderChanged (this, view);
});
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
bool CViewContainer::invalidateDirtyViews ()
{
if (!isVisible ())
return true;
if (CView::isDirty ())
{
if (auto parent = getParentView ())
parent->invalidRect (getViewSize ());
return true;
}
for (const auto& pV : pImpl->children)
{
if (pV->isDirty () && pV->isVisible ())
{
if (CViewContainer* container = pV->asViewContainer ())
container->invalidateDirtyViews ();
else
pV->invalid ();
}
}
return true;
}
//-----------------------------------------------------------------------------
void CViewContainer::invalid ()
{
if (!isVisible ())
return;
CRect _rect (getViewSize ());
if (auto parent = getParentView ())
parent->invalidRect (_rect);
}
//-----------------------------------------------------------------------------
void CViewContainer::invalidRect (const CRect& rect)
{
if (!isVisible ())
return;
CRect _rect (rect);
getTransform ().transform (_rect);
_rect.offset (getViewSize ().left, getViewSize ().top);
_rect.bound (getViewSize ());
if (_rect.isEmpty ())
return;
if (auto parent = getParentView ())
parent->invalidRect (_rect);
}
//-----------------------------------------------------------------------------
/**
* @param pContext the context which to use to draw this container and its subviews
*/
void CViewContainer::draw (CDrawContext* pContext)
{
CViewContainer::drawRect (pContext, getViewSize ());
}
//-----------------------------------------------------------------------------
/**
* @param pContext the context which to use to draw the background
* @param _updateRect the area which to draw
*/
void CViewContainer::drawBackgroundRect (CDrawContext* pContext, const CRect& _updateRect)
{
if (getDrawBackground ())
{
drawClipped (pContext, _updateRect, [&] () {
CRect tr (0, 0, getViewSize ().getWidth (), getViewSize ().getHeight ());
getDrawBackground ()->draw (pContext, tr, getBackgroundOffset ());
});
}
else if ((pImpl->backgroundColor.alpha != 255 && getTransparency ()) || !getTransparency ())
{
pContext->setDrawMode (kAliasing);
pContext->setLineWidth (1);
pContext->setFillColor (pImpl->backgroundColor);
pContext->setFrameColor (pImpl->backgroundColor);
pContext->setLineStyle (kLineSolid);
CRect r;
if (pImpl->backgroundColorDrawStyle == kDrawFilled || (pImpl->backgroundColorDrawStyle == kDrawFilledAndStroked && pImpl->backgroundColor.alpha == 255))
{
r = _updateRect;
r.extend (1, 1);
}
else
{
r = getViewSize ();
r.offset (-r.left, -r.top);
}
pContext->drawRect (r, pImpl->backgroundColorDrawStyle);
}
}
//-----------------------------------------------------------------------------
/**
* @param pContext the context which to use to draw
* @param updateRect the area which to draw
*/
void CViewContainer::drawRect (CDrawContext* pContext, const CRect& updateRect)
{
CPoint offset (getViewSize ().left, getViewSize ().top);
CDrawContext::Transform offsetTransform (*pContext, CGraphicsTransform ().translate (offset.x, offset.y));
CRect _updateRect (updateRect);
_updateRect.bound (getViewSize ());
CRect clientRect (_updateRect);
clientRect.offset (-getViewSize ().left, -getViewSize ().top);
CRect oldClip;
pContext->getClipRect (oldClip);
CRect oldClip2 (oldClip);
CRect newClip (clientRect);
newClip.bound (oldClip);
pContext->setClipRect (newClip);
// draw the background
drawBackgroundRect (pContext, clientRect);
CView* _focusView = nullptr;
IFocusDrawing* _focusDrawing = nullptr;
auto frame = getFrame ();
if (frame && frame->focusDrawingEnabled () && isChild (frame->getFocusView (), false) && frame->getFocusView ()->isVisible () && frame->getFocusView ()->wantsFocus ())
{
_focusView = frame->getFocusView ();
_focusDrawing = dynamic_cast<IFocusDrawing*> (_focusView);
}
{
CDrawContext::Transform tr (*pContext, getTransform ());
getTransform ().inverse ().transform (newClip);
getTransform ().inverse ().transform (clientRect);
getTransform ().transform (oldClip2);
// draw each view
for (const auto& pV : pImpl->children)
{
if (pV->isVisible ())
{
if (frame && _focusDrawing && _focusView == pV && !_focusDrawing->drawFocusOnTop ())
{
SharedPointer<CGraphicsPath> focusPath = owned (pContext->createGraphicsPath ());
if (focusPath)
{
if (_focusDrawing->getFocusPath (*focusPath))
{
auto lastDrawnFocus = focusPath->getBoundingBox ();
if (!lastDrawnFocus.isEmpty ())
{
pContext->setClipRect (oldClip2);
pContext->setDrawMode (kAntiAliasing|kNonIntegralMode);
pContext->setFillColor (frame->getFocusColor ());
pContext->drawGraphicsPath (focusPath, CDrawContext::kPathFilledEvenOdd);
lastDrawnFocus.extend (1, 1);
setLastDrawnFocus (lastDrawnFocus);
}
_focusDrawing = nullptr;
_focusView = nullptr;
}
}
}
if (checkUpdateRect (pV, clientRect))
{
CRect viewSize = pV->getViewSize ();
viewSize.bound (newClip);
if (viewSize.getWidth () == 0 || viewSize.getHeight () == 0)
continue;
pContext->setClipRect (viewSize);
float globalContextAlpha = pContext->getGlobalAlpha ();
pContext->setGlobalAlpha (globalContextAlpha * pV->getAlphaValue ());
pV->drawRect (pContext, viewSize);
pContext->setGlobalAlpha (globalContextAlpha);
#if DEBUG
static bool drawViewWireFrames = false;
if (drawViewWireFrames)
{
pContext->setFrameColor (kRedCColor);
pContext->setLineWidth (pContext->getHairlineSize ());
pContext->setLineStyle (kLineSolid);
pContext->drawRect (pV->getViewSize ());
}
#endif
}
}
}
}
pContext->setClipRect (oldClip2);
if (frame && _focusView)
{
SharedPointer<CGraphicsPath> focusPath = owned (pContext->createGraphicsPath ());
if (focusPath)
{
if (_focusDrawing)
_focusDrawing->getFocusPath (*focusPath);
else
{
CCoord focusWidth = frame->getFocusWidth ();
CRect r (_focusView->getVisibleViewSize ());
if (!r.isEmpty ())
{
focusPath->addRect (r);
r.extend (focusWidth, focusWidth);
focusPath->addRect (r);
}
}
auto lastDrawnFocus = focusPath->getBoundingBox ();
if (!lastDrawnFocus.isEmpty ())
{
pContext->setDrawMode (kAntiAliasing|kNonIntegralMode);
pContext->setFillColor (frame->getFocusColor ());
pContext->drawGraphicsPath (focusPath, CDrawContext::kPathFilledEvenOdd);
lastDrawnFocus.extend (1, 1);
setLastDrawnFocus (lastDrawnFocus);
}
}
}
setDirty (false);
}
//-----------------------------------------------------------------------------
/**
* check if view needs to be updated for rect
* @param view view to check
* @param rect update rect
* @return true if view needs update
*/
bool CViewContainer::checkUpdateRect (CView* view, const CRect& rect)
{
return view->checkUpdate (rect) && view->isVisible ();
}
//-----------------------------------------------------------------------------
/**
* @param where point
* @param event current event
* @return true if any sub view accepts the hit
*/
bool CViewContainer::hitTestSubViews (const CPoint& where, const Event& event)
{
CPoint where2 (where);
where2.offset (-getViewSize ().left, -getViewSize ().top);
getTransform ().inverse ().transform (where2);
for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; ++it)
{
const auto& pV = *it;
if (pV && pV->isVisible () && pV->getMouseEnabled () && pV->hitTest (where2, event))
{
if (auto container = pV->asViewContainer ())
{
if (container->hitTestSubViews (where2, event))
return true;
}
else
return true;
}
}
return false;
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
bool CViewContainer::onWheel (const CPoint& where, const CMouseWheelAxis& axis,
const float& distance, const CButtonState& buttons)
{
return false;
}
#endif
//------------------------------------------------------------------------
void CViewContainer::dispatchEventToSubViews (Event& event)
{
if (auto mouseEvent = asMousePositionEvent (event))
{
auto mousePos = mouseEvent->mousePosition;
auto f = finally ([&] () { mouseEvent->mousePosition = mousePos; });
mouseEvent->mousePosition.offset (-getViewSize ().left, -getViewSize ().top);
getTransform ().inverse ().transform (mouseEvent->mousePosition);
for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end;
++it)
{
const auto& pV = *it;
if (pV && pV->isVisible () && pV->getMouseEnabled () &&
pV->getMouseableArea ().pointInside (mouseEvent->mousePosition))
{
pV->dispatchEvent (event);
if (!pV->getTransparency () || event.consumed)
return;
}
}
}
}
//------------------------------------------------------------------------
void CViewContainer::onMouseWheelEvent (MouseWheelEvent& event)
{
dispatchEventToSubViews (event);
}
//------------------------------------------------------------------------
void CViewContainer::onZoomGestureEvent (ZoomGestureEvent& event)
{
dispatchEventToSubViews (event);
}
//------------------------------------------------------------------------
void CViewContainer::onMouseDownEvent (MouseDownEvent& event)
{
auto buttonState = buttonStateFromMouseEvent (event);
auto mouseResult = onMouseDown (event.mousePosition, buttonState);
if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented))
{
event.consumed = true;
if (mouseResult == kMouseMoveEventHandledButDontNeedMoreEvents)
event.ignoreFollowUpMoveAndUpEvents (true);
return;
}
auto f = finally ([&, pos = event.mousePosition] () { event.mousePosition = pos; });
event.mousePosition.offset (-getViewSize ().left, -getViewSize ().top);
getTransform ().inverse ().transform (event.mousePosition);
for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; ++it)
{
const auto& pV = *it;
if (pV && pV->isVisible () && pV->getMouseEnabled () &&
pV->hitTest (event.mousePosition, event))
{
if (!event.modifiers.empty ())
{
if (auto control = pV.cast<CControl> ())
{
if (auto listener = control->getListener ())
{
if (listener->controlModifierClicked (control, buttonState) != 0)
{
event.consumed = true;
return;
}
}
}
}
auto frame = getFrame ();
auto previousFocusView = frame ? frame->getFocusView () : nullptr;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
mouseResult = pV->callMouseListener (MouseListenerCall::MouseDown, event.mousePosition, buttonState);
if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented))
{
event.consumed = true;
if (mouseResult == kMouseMoveEventHandledButDontNeedMoreEvents)
event.ignoreFollowUpMoveAndUpEvents (true);
return;
}
#endif
pV->dispatchEvent (event);
if (event.consumed)
{
if (pV->getNbReference () >1)
{
if (pV->wantsFocus () && frame && frame->getFocusView () == previousFocusView &&
dynamic_cast<CControl*> (pV.get ()))
{
getFrame ()->setFocusView (pV);
}
if (!event.ignoreFollowUpMoveAndUpEvents ())
setMouseDownView (pV);
}
return;
}
if (!pV->getTransparency ())
return;
}
}
}
//------------------------------------------------------------------------
void CViewContainer::onMouseMoveEvent (MouseMoveEvent& event)
{
auto buttonState = buttonStateFromMouseEvent (event);
auto mouseResult = onMouseMoved (event.mousePosition, buttonState);
if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented))
{
event.consumed = true;
if (mouseResult == kMouseMoveEventHandledButDontNeedMoreEvents)
event.ignoreFollowUpMoveAndUpEvents (true);
return;
}
if (auto view = shared (getMouseDownView ()))
{
auto f = finally ([&, pos = event.mousePosition] () { event.mousePosition = pos; });
event.mousePosition.offset (-getViewSize ().left, -getViewSize ().top);
getTransform ().inverse ().transform (event.mousePosition);
#if VSTGUI_ENABLE_DEPRECATED_METHODS
mouseResult = view->callMouseListener (MouseListenerCall::MouseMoved, event.mousePosition,
buttonState);
if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented))
{
event.consumed = true;
if (mouseResult == kMouseMoveEventHandledButDontNeedMoreEvents)
event.ignoreFollowUpMoveAndUpEvents (true);
return;
}
#endif
view->dispatchEvent (event);
}
}
//------------------------------------------------------------------------
void CViewContainer::onMouseUpEvent (MouseUpEvent& event)
{
auto buttonState = buttonStateFromMouseEvent (event);
auto mouseResult = onMouseUp (event.mousePosition, buttonState);
if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented))
{
event.consumed = true;
return;
}
if (auto view = shared (getMouseDownView ()))
{
auto f = finally ([&, pos = event.mousePosition] () { event.mousePosition = pos; });
event.mousePosition.offset (-getViewSize ().left, -getViewSize ().top);
getTransform ().inverse ().transform (event.mousePosition);
#if VSTGUI_ENABLE_DEPRECATED_METHODS
mouseResult =
view->callMouseListener (MouseListenerCall::MouseUp, event.mousePosition, buttonState);
if (!(mouseResult == kMouseEventNotHandled || mouseResult == kMouseEventNotImplemented))
{
event.consumed = true;
return;
}
#endif
view->dispatchEvent (event);
clearMouseDownView ();
}
}
//-----------------------------------------------------------------------------
void CViewContainer::onMouseCancelEvent (MouseCancelEvent& event)
{
if (auto mouseDownView = getMouseDownView ())
{
CBaseObjectGuard crg (mouseDownView);
#if VSTGUI_ENABLE_DEPRECATED_METHODS
mouseDownView->callMouseListener (MouseListenerCall::MouseCancel, {}, 0);
#endif
mouseDownView->dispatchEvent (event);
clearMouseDownView ();
}
}
//-----------------------------------------------------------------------------
SharedPointer<IDropTarget> CViewContainer::getDropTarget ()
{
if (getFrame () == this)
{
IDropTarget* dropTarget = nullptr;
if (!getAttribute (kCViewContainerDropTargetAttribute, dropTarget))
{
dropTarget = new CViewContainerDropTarget (this);
setAttribute (kCViewContainerDropTargetAttribute, dropTarget);
}
return dropTarget;
}
if (auto customDropTarget = CView::getDropTarget ())
return customDropTarget;
return makeOwned<CViewContainerDropTarget> (this);
}
#if VSTGUI_TOUCH_EVENT_HANDLING
//-----------------------------------------------------------------------------
void CViewContainer::onTouchEvent (ITouchEvent& event)
{
ReverseViewIterator it (this);
while (*it)
{
CView* view = *it;
CBaseObjectGuard guard (view);
if (view->wantsMultiTouchEvents ())
{
for (const auto& e : event)
{
if (e.second.state == ITouchEvent::kBegan && e.second.target == 0)
{
CPoint where (e.second.location);
frameToLocal (where);
MouseDownEvent downEvent (where, MouseButton::Left);
downEvent.clickCount = e.second.tapCount;
if (view->hitTest (where, downEvent))
{
view->onTouchEvent (event);
break;
}
}
}
}
it++;
}
}
//-----------------------------------------------------------------------------
bool CViewContainer::findSingleTouchEventTarget (ITouchEvent::Touch& event)
{
vstgui_assert(event.target == 0);
vstgui_assert(event.state == ITouchEvent::kBegan);
CPoint where (event.location);
frameToLocal (where);
MouseDownEvent downEvent (where, MouseButton::Left);
downEvent.clickCount = event.tapCount;
ReverseViewIterator it (this);
while (*it)
{
CView* view = *it;
CBaseObjectGuard guard (view);
if (view->getMouseEnabled () && view->isVisible () && view->hitTest (where, downEvent))
{
if (auto container = view->asViewContainer ())
{
if (container->findSingleTouchEventTarget (event))
return true;
}
else
{
view->dispatchEvent (downEvent);
if (downEvent.ignoreFollowUpMoveAndUpEvents ())
return true;
else if (downEvent.consumed)
{
event.target = view;
event.targetIsSingleTouch = true;
return true;
}
}
}
it++;
}
return false;
}
#endif
//-----------------------------------------------------------------------------
void CViewContainer::looseFocus ()
{
CView::looseFocus ();
}
//-----------------------------------------------------------------------------
void CViewContainer::takeFocus ()
{
CView::takeFocus ();
}
//------------------------------------------------------------------------
void CViewContainer::setInitialFocusView (CView* view)
{
if (auto oldInitialFocusView = getInitialFocusView ())
{
oldInitialFocusView->forget ();
}
if (view)
{
setAttribute (kInitialFocusViewAttribute, view);
view->remember ();
}
else
{
removeAttribute (kInitialFocusViewAttribute);
}
}
//------------------------------------------------------------------------
CView* CViewContainer::getInitialFocusView () const
{
CView* initialFocusView = nullptr;
getAttribute (kInitialFocusViewAttribute, initialFocusView);
return initialFocusView;
}
//-----------------------------------------------------------------------------
/**
* @param oldFocus old focus view
* @param reverse search order
* @return true on success
*/
bool CViewContainer::advanceNextFocusView (CView* oldFocus, bool reverse)
{
if (auto frame = getFrame ())
{
if (!oldFocus)
{
if (auto initialFocusView = getInitialFocusView ())
{
frame->setFocusView (initialFocusView);
return true;
}
}
bool foundOld = false;
auto func = [&] (CView* pV) {
if (oldFocus && !foundOld)
{
if (oldFocus == pV)
{
foundOld = true;
return false;
}
}
else
{
if (pV->wantsFocus () && pV->getMouseEnabled () && pV->isVisible ())
{
frame->setFocusView (pV);
return true;
}
else if (CViewContainer* container = pV->asViewContainer ())
{
if (container->advanceNextFocusView (nullptr, reverse))
return true;
}
}
return false;
};
if (reverse)
{
for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; ++it)
{
if (func (*it))
return true;
}
}
else
{
for (const auto& view : pImpl->children)
{
if (func (view))
return true;
}
}
}
return false;
}
//-----------------------------------------------------------------------------
bool CViewContainer::isDirty () const
{
if (CView::isDirty ())
return true;
CRect viewSize (getViewSize ());
viewSize.offset (-getViewSize ().left, -getViewSize ().top);
for (const auto& pV : pImpl->children)
{
if (pV->isDirty () && pV->isVisible ())
{
CRect r = pV->getViewSize ();
r.bound (viewSize);
if (r.getWidth () > 0 && r.getHeight () > 0)
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
/**
* @param p location
* @param options search options
* @return view at position p or null
*/
CView* CViewContainer::getViewAt (const CPoint& p, const GetViewOptions& options) const
{
CPoint where (p);
where.offset (-getViewSize ().left, -getViewSize ().top);
getTransform ().inverse ().transform (where);
for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; ++it)
{
const auto& pV = *it;
if (pV && pV->getMouseableArea ().pointInside (where))
{
if (!options.getIncludeInvisible () && pV->isVisible () == false)
continue;
if (options.getMouseEnabled ())
{
if (pV->getMouseEnabled () == false)
continue;
}
if (options.getDeep ())
{
if (auto container = pV->asViewContainer ())
{
CView* view = container->getViewAt (where, options);
return options.getIncludeViewContainer () ? (view ? view : container) : view;
}
}
if (!options.getIncludeViewContainer () && pV->asViewContainer ())
continue;
return pV;
}
}
return nullptr;
}
//-----------------------------------------------------------------------------
/**
* @param p location
* @param views result list
* @param options search options
* @return success
*/
bool CViewContainer::getViewsAt (const CPoint& p, ViewList& views, const GetViewOptions& options) const
{
bool result = false;
CPoint where (p);
where.offset (-getViewSize ().left, -getViewSize ().top);
getTransform ().inverse ().transform (where);
for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; ++it)
{
const auto& pV = *it;
if (pV && pV->getMouseableArea ().pointInside (where))
{
if (!options.getIncludeInvisible () && pV->isVisible () == false)
continue;
if (options.getMouseEnabled ())
{
if (pV->getMouseEnabled () == false)
continue;
}
if (options.getDeep ())
{
if (CViewContainer* container = pV->asViewContainer ())
result |= container->getViewsAt (where, views, options);
}
if (options.getIncludeViewContainer () == false)
{
if (pV->asViewContainer ())
continue;
}
views.emplace_back (pV);
result = true;
}
}
return result;
}
//-----------------------------------------------------------------------------
/**
* @param p location
* @param options search search options
* @return view container at position p or null
*/
CViewContainer* CViewContainer::getContainerAt (const CPoint& p, const GetViewOptions& options) const
{
CPoint where (p);
where.offset (-getViewSize ().left, -getViewSize ().top);
getTransform ().inverse ().transform (where);
for (auto it = pImpl->children.rbegin (), end = pImpl->children.rend (); it != end; ++it)
{
const auto& pV = *it;
if (pV && pV->getMouseableArea ().pointInside (where))
{
if (!options.getIncludeInvisible () && pV->isVisible () == false)
continue;
if (options.getMouseEnabled ())
{
if (pV->getMouseEnabled() == false)
continue;
}
if (options.getDeep ())
{
if (CViewContainer* container = pV->asViewContainer ())
return container->getContainerAt (where, options);
}
break;
}
}
return const_cast<CViewContainer*>(this);
}
//-----------------------------------------------------------------------------
CPoint& CViewContainer::frameToLocal (CPoint& point) const
{
point.offset (-getViewSize ().left, -getViewSize ().top);
if (auto parent = getParentView ())
return parent->frameToLocal (point);
return point;
}
//-----------------------------------------------------------------------------
CPoint& CViewContainer::localToFrame (CPoint& point) const
{
point.offset (getViewSize ().left, getViewSize ().top);
if (auto parent = getParentView ())
return parent->localToFrame (point);
return point;
}
//-----------------------------------------------------------------------------
bool CViewContainer::removed (CView* parent)
{
if (!isAttached ())
return false;
for (const auto& pV : pImpl->children)
pV->removed (this);
return CView::removed (parent);
}
//-----------------------------------------------------------------------------
bool CViewContainer::attached (CView* parent)
{
if (isAttached ())
return false;
setParentFrame (parent->getFrame ());
bool result = CView::attached (parent);
if (result)
{
for (const auto& pV : pImpl->children)
pV->attached (this);
}
if (auto layout = calculateViewLayout (getViewSize ()))
applyViewLayout (*layout);
return result;
}
#if DEBUG
static int32_t _debugDumpLevel = 0;
//-----------------------------------------------------------------------------
void CViewContainer::dumpInfo ()
{
CView::dumpInfo ();
}
//-----------------------------------------------------------------------------
void CViewContainer::dumpHierarchy ()
{
_debugDumpLevel++;
for (auto& pV : pImpl->children)
{
for (int32_t i = 0; i < _debugDumpLevel; i++)
DebugPrint ("\t");
pV->dumpInfo ();
DebugPrint ("\n");
if (CViewContainer* container = pV->asViewContainer ())
container->dumpHierarchy ();
}
_debugDumpLevel--;
}
#endif
} // VSTGUI