Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,546 @@
// 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 "crowcolumnview.h"
#include "animation/animations.h"
#include "animation/timingfunctions.h"
#include <vector>
namespace VSTGUI {
namespace Layouting {
//--------------------------------------------------------------------------------
using CRects = std::vector<CRect>;
enum class Alignment
{
kTopLeft,
kTopCenter,
kTopRight,
kMiddleLeft,
kMiddleCenter,
kMiddleRight,
kBottomLeft,
kBottomCenter,
kBottomRight
};
enum class Style
{
kRow,
kColumn
};
//--------------------------------------------------------------------------------
Alignment translate (const CRowColumnView::LayoutStyle layoutStyle)
{
switch (layoutStyle)
{
case CRowColumnView::LayoutStyle::kTopLeft:
return Alignment::kTopLeft;
case CRowColumnView::LayoutStyle::kTopCenter:
return Alignment::kTopCenter;
case CRowColumnView::LayoutStyle::kTopRight:
return Alignment::kTopRight;
case CRowColumnView::LayoutStyle::kMiddleLeft:
return Alignment::kMiddleLeft;
case CRowColumnView::LayoutStyle::kMiddleCenter:
return Alignment::kMiddleCenter;
case CRowColumnView::LayoutStyle::kMiddleRight:
return Alignment::kMiddleRight;
case CRowColumnView::LayoutStyle::kBottomLeft:
return Alignment::kBottomLeft;
case CRowColumnView::LayoutStyle::kBottomCenter:
return Alignment::kBottomCenter;
case CRowColumnView::LayoutStyle::kBottomRight:
return Alignment::kBottomRight;
default:
return Alignment::kTopLeft;
}
}
//--------------------------------------------------------------------------------
Style translate (const CRowColumnView::Style style)
{
switch (style)
{
case CRowColumnView::Style::kRowStyle:
return Style::kRow;
case CRowColumnView::Style::kColumnStyle:
return Style::kColumn;
default:
return Style::kRow;
}
}
//--------------------------------------------------------------------------------
CPoint computeRectOffset (const CPoint& parent, const CPoint& rect, const Alignment alignment)
{
CPoint offset (0., 0.);
switch (alignment)
{
case Alignment::kTopLeft:
{
offset = CPoint (0., 0.);
break;
}
case Alignment::kTopCenter:
{
offset = CPoint ((parent.x - rect.x) / 2., 0.);
break;
}
case Alignment::kTopRight:
{
offset = CPoint (parent.x - rect.x, 0.);
break;
}
case Alignment::kMiddleLeft:
{
offset = CPoint (0., (parent.y - rect.y) / 2.);
break;
}
case Alignment::kMiddleCenter:
{
offset = CPoint ((parent.x - rect.x) / 2., (parent.y - rect.y) / 2.);
break;
}
case Alignment::kMiddleRight:
{
offset = CPoint (parent.x - rect.x, (parent.y - rect.y) / 2.);
break;
}
case Alignment::kBottomLeft:
{
offset = CPoint (0., parent.y - rect.y);
break;
}
case Alignment::kBottomCenter:
{
offset = CPoint ((parent.x - rect.x) / 2., parent.y - rect.y);
break;
}
case Alignment::kBottomRight:
{
offset = CPoint (parent.x - rect.x, parent.y - rect.y);
break;
}
default:
break;
}
return offset;
}
//--------------------------------------------------------------------------------
CRect computeGroupRect (const CRect& parent, const CRects& children, const Alignment alignment,
const Style style, double spacing)
{
CPoint maxSize;
if (style == Style::kRow)
{
for (const auto& rect : children)
{
if (maxSize.x < rect.getWidth ())
maxSize.x = rect.getWidth ();
maxSize.y = maxSize.y + rect.getHeight () + spacing;
}
// Remove the last spacing again
if (!children.empty ())
maxSize.y -= spacing;
}
else
{
for (const auto& rect : children)
{
if (maxSize.y < rect.getHeight ())
maxSize.y = rect.getHeight ();
maxSize.x = maxSize.x + rect.getWidth () + spacing;
}
// Remove the last spacing again
if (!children.empty ())
maxSize.x -= spacing;
}
const auto offset = computeRectOffset (parent.getSize (), maxSize, alignment);
return CRect ().setSize (maxSize).offset (offset);
}
//--------------------------------------------------------------------------------
CRect computeGroupRect (const CViewContainer& parent, const Alignment alignment, const Style style,
const double spacing)
{
CRects childrenViewSizes;
parent.forEachChild (
[&] (const CView* child) { childrenViewSizes.push_back (child->getViewSize ()); });
return computeGroupRect (parent.getViewSize (), childrenViewSizes, alignment, style, spacing);
}
//--------------------------------------------------------------------------------
// AutoLayout Declaration
/// @brief An auto layout feature for the CRowColumnView
///
/// The AutoLayout flexibly layouts the children of a CRowColumnView. The children are grouped and
/// aligned among themselves depending on whether the style is a row (left to right) or a
/// column (top to bottom). The group is moved inside the parent to either of the nine
/// positions (top-left, top-center, top-right, middle-left, middle-center, middle-right,
/// bottom-left, bottom-center or bottom-right). If the size of the parent is changed, the layout
/// and alignment of the group is always retained.
//--------------------------------------------------------------------------------
class AutoLayout
{
public:
//--------------------------------------------------------------------------------
AutoLayout (const CViewContainer& parent, const Alignment alignment, const Style style,
const double spacing)
: alignment (alignment), style (style)
{
groupRect = Layouting::computeGroupRect (parent, alignment, style, spacing);
}
/** moves the child rect to the calculated position (inside the group and inside the parent) */
auto moveRect (CRect& viewSize) -> CRect&
{
// Offset the viewSize inside the groupRect...
const CPoint offset =
Layouting::computeRectOffset (groupRect.getSize (), viewSize.getSize (), alignment);
if (style == Style::kRow)
viewSize.offset (offset.x, 0.);
else
viewSize.offset (0., offset.y);
//...and offset by topLeft of the groupRect afterwards, in order to align with the
// 'real' parent.
return viewSize.offset (groupRect.getTopLeft ());
}
//--------------------------------------------------------------------------------
private:
const Alignment alignment = Alignment::kTopLeft;
const Style style = Style::kRow;
CRect groupRect;
};
//--------------------------------------------------------------------------------
}
//--------------------------------------------------------------------------------
CRowColumnView::CRowColumnView (const CRect& size, Style style, LayoutStyle layoutStyle,
CCoord spacing, const CRect& margin)
: CAutoLayoutContainerView (size)
, style (style)
, layoutStyle (layoutStyle)
, spacing (spacing)
, margin (margin)
, flags (0)
, layoutGuard (false)
, viewResizeAnimationTime (200)
{
}
//--------------------------------------------------------------------------------
void CRowColumnView::setStyle (Style newStyle)
{
if (newStyle != style)
{
style = newStyle;
if (isAttached ())
layoutViews ();
}
}
//--------------------------------------------------------------------------------
void CRowColumnView::setSpacing (CCoord newSpacing)
{
if (newSpacing != spacing)
{
spacing = newSpacing;
if (isAttached ())
layoutViews ();
}
}
//--------------------------------------------------------------------------------
void CRowColumnView::setMargin (const CRect& newMargin)
{
if (newMargin != margin)
{
margin = newMargin;
if (isAttached ())
layoutViews ();
}
}
//--------------------------------------------------------------------------------
void CRowColumnView::setLayoutStyle (LayoutStyle inLayoutStyle)
{
if (inLayoutStyle != layoutStyle)
{
layoutStyle = inLayoutStyle;
if (isAttached ())
layoutViews ();
}
}
//--------------------------------------------------------------------------------
bool CRowColumnView::isAnimateViewResizing () const { return hasBit (flags, kAnimateViewResizing); }
//--------------------------------------------------------------------------------
void CRowColumnView::setAnimateViewResizing (bool state)
{
setBit (flags, kAnimateViewResizing, state);
}
//--------------------------------------------------------------------------------
bool CRowColumnView::hideClippedSubviews () const { return hasBit (flags, kHideClippedSubViews); }
//--------------------------------------------------------------------------------
void CRowColumnView::setHideClippedSubviews (bool state)
{
setBit (flags, kHideClippedSubViews, state);
}
//--------------------------------------------------------------------------------
void CRowColumnView::resizeSubView (CView* view, const CRect& newSize)
{
if (view->getViewSize () != newSize)
{
if (isAttached () && isAnimateViewResizing () && viewResizeAnimationTime > 0)
{
view->setAttribute (ViewAnimatesResizingAttr, true);
view->addAnimation (
"CRowColumnResizing", new Animation::ViewSizeAnimation (newSize, false),
new Animation::LinearTimingFunction (viewResizeAnimationTime),
[] (CView* v, auto, auto) { v->removeAttribute (ViewAnimatesResizingAttr); }, true);
}
else
{
view->setViewSize (newSize);
view->setMouseableArea (newSize);
}
}
}
//--------------------------------------------------------------------------------
void CRowColumnView::getMaxChildViewSize (CPoint& maxSize)
{
forEachChild ([&] (CView* view) {
const CRect& viewSize = view->getViewSize ();
if (viewSize.getWidth () > maxSize.x)
maxSize.x = viewSize.getWidth ();
if (viewSize.getHeight () > maxSize.y)
maxSize.y = viewSize.getHeight ();
});
}
//--------------------------------------------------------------------------------
void CRowColumnView::layoutViewsEqualSize ()
{
CPoint maxSize;
getMaxChildViewSize (maxSize);
if (style == kRowStyle)
maxSize.x = getViewSize ().getWidth () - (margin.right + margin.left);
else
maxSize.y = getViewSize ().getHeight () - (margin.top + margin.bottom);
Layouting::AutoLayout layout (*this, Layouting::translate (layoutStyle),
Layouting::translate (style), spacing);
CPoint location = margin.getTopLeft ();
forEachChild ([&] (CView* view) {
CRect viewSize = view->getViewSize ();
viewSize.originize ();
viewSize.offset (location.x, location.y);
switch (layoutStyle)
{
case kStretchEqualy:
{
viewSize.setWidth (maxSize.x);
viewSize.setHeight (maxSize.y);
break;
}
case kCenterEqualy:
{
CCoord diffX = (maxSize.x - viewSize.getWidth ()) / 2.;
CCoord diffY = (maxSize.y - viewSize.getHeight ()) / 2.;
viewSize.offset (diffX, diffY);
break;
}
case kRightBottomEqualy:
{
CCoord diffX = maxSize.x - viewSize.getWidth ();
CCoord diffY = maxSize.y - viewSize.getHeight ();
viewSize.offset (diffX, diffY);
break;
}
case kTopLeft:
case kTopCenter:
case kTopRight:
case kMiddleLeft:
case kMiddleCenter:
case kMiddleRight:
case kBottomLeft:
case kBottomCenter:
case kBottomRight:
{
layout.moveRect (viewSize);
break;
}
default:
break;
}
resizeSubView (view, viewSize);
if (style == kRowStyle)
{
location.y += spacing;
location.y += viewSize.getHeight ();
}
else
{
location.x += spacing;
location.x += viewSize.getWidth ();
}
});
}
//--------------------------------------------------------------------------------
void CRowColumnView::layoutViews ()
{
if (layoutGuard == false)
{
layoutGuard = true;
layoutViewsEqualSize ();
if (hideClippedSubviews ())
{
forEachChild ([&] (CView* view) {
if (view->getVisibleViewSize () != view->getViewSize ())
view->setVisible (false);
else
view->setVisible (true);
});
}
layoutGuard = false;
}
}
//--------------------------------------------------------------------------------
bool CRowColumnView::sizeToFit ()
{
if (!getChildren ().empty ())
{
CRect viewSize = getViewSize ();
CPoint maxSize;
if (style == kRowStyle)
{
forEachChild ([&] (CView* view) {
const CRect& size = view->getViewSize ();
if (size.getWidth () > maxSize.x)
maxSize.x = size.getWidth ();
maxSize.y += size.getHeight () + spacing;
});
}
else
{
forEachChild ([&] (CView* view) {
CRect size = view->getViewSize ();
maxSize.x += size.getWidth () + spacing;
if (size.bottom > maxSize.y)
maxSize.y = size.getHeight ();
});
}
viewSize.setWidth (maxSize.x + margin.left + margin.right);
viewSize.setHeight (maxSize.y + margin.top + margin.bottom);
if (viewSize != getViewSize ())
{
invalid ();
auto autosizeState = getAutosizingEnabled ();
setAutosizingEnabled (false);
CViewContainer::setViewSize (viewSize);
CViewContainer::setMouseableArea (viewSize);
setAutosizingEnabled (autosizeState);
invalid ();
}
return true;
}
return false;
}
//--------------------------------------------------------------------------------
CMessageResult CRowColumnView::notify (CBaseObject* sender, IdStringPtr message)
{
if (message == kMsgViewSizeChanged)
{
bool animatesResizing = false;
static_cast<CView*> (sender)->getAttribute (ViewAnimatesResizingAttr, animatesResizing);
if (isAttached () && !animatesResizing)
layoutViews ();
}
return CViewContainer::notify (sender, message);
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
CAutoLayoutContainerView::CAutoLayoutContainerView (const CRect& size) : CViewContainer (size) {}
//--------------------------------------------------------------------------------
bool CAutoLayoutContainerView::attached (CView* parent)
{
if (!isAttached ())
{
layoutViews ();
return CViewContainer::attached (parent);
}
return false;
}
//--------------------------------------------------------------------------------
void CAutoLayoutContainerView::setViewSize (const CRect& rect, bool invalid)
{
CViewContainer::setViewSize (rect, invalid);
if (isAttached ())
layoutViews ();
}
//--------------------------------------------------------------------------------
bool CAutoLayoutContainerView::addView (CView* pView, CView* pBefore)
{
if (CViewContainer::addView (pView, pBefore))
{
if (isAttached ())
layoutViews ();
return true;
}
return false;
}
//--------------------------------------------------------------------------------
bool CAutoLayoutContainerView::removeView (CView* pView, bool withForget)
{
if (CViewContainer::removeView (pView, withForget))
{
if (isAttached ())
layoutViews ();
return true;
}
return false;
}
//--------------------------------------------------------------------------------
bool CAutoLayoutContainerView::changeViewZOrder (CView* view, uint32_t newIndex)
{
if (CViewContainer::changeViewZOrder (view, newIndex))
{
if (isAttached ())
layoutViews ();
return true;
}
return false;
}
} // VSTGUI