// 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 "ctabview.h" #include "cfont.h" #include "cbitmap.h" #include "cdrawcontext.h" #include "dragging.h" #include "controls/cbuttons.h" #include "cgraphicspath.h" namespace VSTGUI { #define kTabButtonTagStart 20000 /// @cond ignore //----------------------------------------------------------------------------- class CTabButton : public COnOffButton, public DropTargetAdapter //----------------------------------------------------------------------------- { public: CTabButton (const CRect &size, IControlListener *listener, int32_t tag, CBitmap *background, UTF8StringPtr inName) : COnOffButton (size, listener, tag, background) , name (inName) { activeTextColor = kBlackCColor; inactiveTextColor (90, 90, 90, 255); textFont = kSystemFont; textFont->remember (); } ~CTabButton () noexcept override { if (textFont) textFont->forget (); } void draw (CDrawContext *pContext) override { COnOffButton::draw (pContext); if (!name.empty ()) { pContext->setFont (textFont); pContext->setFontColor (value > 0.f ? activeTextColor : inactiveTextColor); pContext->drawString (name, getViewSize ()); } } CMouseEventResult onMouseDown (CPoint &where, const CButtonState& button) override { value = ((int32_t)value) ? 0.f : 1.f; valueChanged (); return kMouseDownEventHandledButDontNeedMovedOrUpEvents; } SharedPointer getDropTarget () override { return this; } DragOperation onDragEnter (DragEventData data) override { if (value == 0.f) { value = 1.f; valueChanged (); } return DragOperation::None; } void setTextFont (CFontRef font) { if (textFont) textFont->forget (); textFont = font; textFont->remember ();} void setActiveTextColor (const CColor& color) { activeTextColor = color; } void setInactiveTextColor (const CColor& color) { inactiveTextColor = color; } CLASS_METHODS (CTabButton, COnOffButton) protected: UTF8String name; CFontRef textFont; CColor activeTextColor; CColor inactiveTextColor; }; //----------------------------------------------------------------------------- class CTabChildView : public NonAtomicReferenceCounted //----------------------------------------------------------------------------- { public: explicit CTabChildView (CView* view) : view (view) , previous (nullptr) , next (nullptr) , button (nullptr) { } ~CTabChildView () noexcept override { view->forget (); } CView* view; CTabChildView* previous; CTabChildView* next; CControl* button; }; /// @endcond //----------------------------------------------------------------------------- CTabView::CTabView (const CRect& size, CBitmap* tabBitmap, CBitmap* background, TabPosition tabPosition, int32_t style) : CViewContainer (size) , numberOfChilds (0) , tabPosition (tabPosition) , style (style) , tabSize (CRect (0, 0, 0, 0)) , tabBitmap (tabBitmap) , firstChild (nullptr) , lastChild (nullptr) , currentChild (nullptr) { setBackground (background); if (tabBitmap) { tabBitmap->remember (); tabSize.right = tabBitmap->getWidth (); tabSize.bottom = tabBitmap->getHeight (); } setTransparency (true); } //----------------------------------------------------------------------------- CTabView::CTabView (const CRect& size, const CRect& tabSize, CBitmap* background, TabPosition tabPosition, int32_t style) : CViewContainer (size) , numberOfChilds (0) , currentTab (-1) , tabPosition (tabPosition) , style (style) , tabSize (tabSize) , tabBitmap (nullptr) , firstChild (nullptr) , lastChild (nullptr) , currentChild (nullptr) { setBackground (background); setTransparency (true); } //----------------------------------------------------------------------------- CTabView::~CTabView () noexcept { setParentView (nullptr); setParentFrame (nullptr); removeAllTabs (); if (tabBitmap) tabBitmap->forget (); } //----------------------------------------------------------------------------- void CTabView::setAutosizeFlags (int32_t flags) { CViewContainer::setAutosizeFlags (flags); } //----------------------------------------------------------------------------- bool CTabView::addTab (CView* view, UTF8StringPtr name, CBitmap* inTabBitmap) { if (!view) return false; if (inTabBitmap == nullptr) inTabBitmap = tabBitmap; auto* b = new CTabButton (CRect (0, 0, 0, 0), nullptr, 0, inTabBitmap, name); b->setTransparency (true); return addTab (view, b); } //----------------------------------------------------------------------------- bool CTabView::addTab (CView* view, CControl* button) { if (!view || !button) return false; CViewContainer* tabContainer = hasChildren () ? getView (0)->asViewContainer () : nullptr; if (tabContainer == nullptr) { int32_t asf = kAutosizeLeft | kAutosizeTop | kAutosizeRight | kAutosizeColumn; CRect tsc (0, 0, getViewSize ().getWidth (), tabSize.getHeight () / 2); switch (tabPosition) { case kPositionBottom: { asf = kAutosizeLeft | kAutosizeBottom | kAutosizeRight | kAutosizeColumn; tsc.offset (0, getViewSize ().getHeight () - tabSize.getHeight () / 2); break; } case kPositionLeft: { asf = kAutosizeLeft | kAutosizeTop | kAutosizeBottom | kAutosizeRow; tsc.setWidth (tabSize.getWidth ()); tsc.setHeight (getViewSize ().getHeight ()); break; } case kPositionRight: { asf = kAutosizeRight | kAutosizeTop | kAutosizeBottom | kAutosizeRow; tsc.setWidth (getViewSize ().getWidth ()); tsc.left = tsc.right - tabSize.getWidth (); tsc.setHeight (getViewSize ().getHeight ()); break; } case kPositionTop: { break; } } tabContainer = new CViewContainer (tsc); tabContainer->setTransparency (true); tabContainer->setAutosizeFlags (asf); addView (tabContainer); } CRect ts (tabSize.left, tabSize.top, tabSize.getWidth (), tabSize.getHeight () / 2); switch (tabPosition) { case kPositionTop: ts.offset (tabSize.getWidth () * numberOfChilds, 0); break; case kPositionBottom: ts.offset (tabSize.getWidth () * numberOfChilds, 0); break; case kPositionLeft: ts.offset (0, tabSize.getHeight () / 2 * numberOfChilds); break; case kPositionRight: ts.offset (0, tabSize.getHeight () / 2 * numberOfChilds); break; } button->setViewSize (ts, false); button->setMouseableArea (ts); button->setListener (this); button->setTag (numberOfChilds + kTabButtonTagStart); tabContainer->addView (button); CRect tabViewSize; getTabViewSize (tabViewSize); view->setViewSize (tabViewSize); view->setMouseableArea (tabViewSize); auto* v = new CTabChildView (view); v->button = button; if (lastChild) { lastChild->next = v; v->previous = lastChild; lastChild = v; } else { firstChild = lastChild = v; setCurrentChild (v); } numberOfChilds++; return true; } //----------------------------------------------------------------------------- bool CTabView::removeTab (CView* view) { if (!view) return false; CViewContainer* tabContainer = hasChildren () ? getView (0)->asViewContainer () : nullptr; if (!tabContainer) return false; CTabChildView* v = firstChild; while (v) { if (v->view == view) { if (v->previous) v->previous->next = v->next; if (v->next) v->next->previous = v->previous; if (v == currentChild) { setCurrentChild (v->previous ? v->previous : v->next); if (v->previous == nullptr && v->next == nullptr) currentTab = -1; } tabContainer->removeView (v->button, true); v->forget (); numberOfChilds--; return true; } v = v->next; } return false; } //----------------------------------------------------------------------------- bool CTabView::removeAllTabs () { setCurrentChild (nullptr); CTabChildView* v = lastChild; while (v) { CTabChildView* next = v->previous; removeTab (v->view); v = next; } firstChild = nullptr; lastChild = nullptr; numberOfChilds = 0; currentTab = -1; return true; } //----------------------------------------------------------------------------- bool CTabView::selectTab (int32_t index) { if (index > numberOfChilds) return false; CTabChildView* v = firstChild; int32_t i = 0; while (v) { if (index == i) break; v = v->next; i++; } if (v) { setCurrentChild (v); currentTab = i; return true; } return false; } //----------------------------------------------------------------------------- void CTabView::setCurrentChild (CTabChildView* childView) { if (currentChild) { if (childView == currentChild) { if (currentChild->button) currentChild->button->setValue (1.f); return; } if (currentChild->button) currentChild->button->setValue (0.f); removeView (currentChild->view, false); } currentChild = childView; if (currentChild) { addView (currentChild->view); if (currentChild->button) currentChild->button->setValue (1.f); } invalid (); } //----------------------------------------------------------------------------- void CTabView::drawBackgroundRect (CDrawContext *pContext, const CRect& _updateRect) { CRect oldClip = pContext->getClipRect (oldClip); CRect updateRect (_updateRect); CViewContainer* tabContainer = hasChildren () ? getView (0)->asViewContainer () : nullptr; if (tabContainer) { CRect tcRect = tabContainer->getViewSize (); if (updateRect.top < tcRect.bottom) updateRect.top = tcRect.bottom; } pContext->setClipRect (updateRect); CViewContainer::drawBackgroundRect (pContext, updateRect); pContext->setClipRect (oldClip); } //----------------------------------------------------------------------------- void CTabView::valueChanged (CControl *pControl) { selectTab (pControl->getTag () - kTabButtonTagStart); } //----------------------------------------------------------------------------- void CTabView::setTabViewInsets (const CPoint& inset) { tabViewInset = inset; } //----------------------------------------------------------------------------- CRect& CTabView::getTabViewSize (CRect& rect) const { rect = getViewSize (); rect.originize (); switch (tabPosition) { case kPositionTop: rect.top += tabSize.getHeight () / 2; break; case kPositionBottom: rect.bottom -= tabSize.getHeight () / 2; break; case kPositionLeft: rect.left += tabSize.getWidth (); break; case kPositionRight: rect.right -= tabSize.getWidth (); break; } rect.inset (tabViewInset.x, tabViewInset.y); return rect; } //----------------------------------------------------------------------------- void CTabView::setTabFontStyle (const CFontRef font, CCoord fontSize, CColor selectedColor, CColor deselectedColor) { auto tabFont = makeOwned (*font); tabFont->setSize (fontSize); CTabChildView* v = firstChild; while (v) { if (auto* button = dynamic_cast(v->button)) { button->setTextFont (tabFont); button->setActiveTextColor (selectedColor); button->setInactiveTextColor (deselectedColor); } v = v->next; } } //----------------------------------------------------------------------------- void CTabView::alignTabs (TabAlignment alignment) { CCoord allTabsWidth; CCoord viewWidth; CCoord offset = 0; CRect ts (tabSize.left, tabSize.top, tabSize.getWidth (), tabSize.getHeight () / 2); if (tabPosition == kPositionTop || tabPosition == kPositionBottom) { allTabsWidth = tabSize.getWidth () * numberOfChilds; viewWidth = getViewSize ().getWidth (); } else { allTabsWidth = (tabSize.getHeight () / 2) * numberOfChilds; viewWidth = getViewSize ().getHeight (); } if (alignment == kAlignCenter) offset = (viewWidth - allTabsWidth) / 2; else if (alignment == kAlignLeft) offset = 0; else if (alignment == kAlignRight) offset = viewWidth - allTabsWidth; if (tabPosition == kPositionTop) ts.offset (offset, 0); else if (tabPosition == kPositionBottom) ts.offset (offset, 0); else if (tabPosition == kPositionLeft) ts.offset (0, offset); else if (tabPosition == kPositionRight) ts.offset (0, offset); CTabChildView* v = firstChild; while (v) { v->button->setViewSize (ts); v->button->setMouseableArea (ts); if (tabPosition == kPositionTop || tabPosition == kPositionBottom) ts.offset (tabSize.getWidth (), 0); else ts.offset (0, tabSize.getHeight () / 2); v = v->next; } setDirty (true); invalid (); } //----------------------------------------------------------------------------- void CTabView::setViewSize (const CRect &rect, bool invalid) { if (rect == getViewSize ()) return; CRect oldSize (getViewSize ()); CCoord widthDelta = rect.getWidth () - oldSize.getWidth (); CCoord heightDelta = rect.getHeight () - oldSize.getHeight (); if (widthDelta != 0 || heightDelta != 0) { uint32_t numSubviews = getNbViews(); int32_t counter = 1; bool treatAsColumn = (getAutosizeFlags () & kAutosizeColumn) ? true : false; bool treatAsRow = (getAutosizeFlags () & kAutosizeRow) ? true : false; CTabChildView* v = firstChild; while (v) { if (v != currentChild) { CView* pV = v->view; int32_t autosize = pV->getAutosizeFlags (); CRect viewSize (pV->getViewSize ()); CRect mouseSize (pV->getMouseableArea ()); if (treatAsColumn) { if (counter) { viewSize.offset (counter * (widthDelta / (numSubviews)), 0); mouseSize.offset (counter * (widthDelta / (numSubviews)), 0); } viewSize.setWidth (viewSize.getWidth () + (widthDelta / (numSubviews))); mouseSize.setWidth (mouseSize.getWidth () + (widthDelta / (numSubviews))); } else if (widthDelta != 0 && autosize & kAutosizeRight) { viewSize.right += widthDelta; mouseSize.right += widthDelta; if (!(autosize & kAutosizeLeft)) { viewSize.left += widthDelta; mouseSize.left += widthDelta; } } if (treatAsRow) { if (counter) { viewSize.offset (0, counter * (heightDelta / (numSubviews))); mouseSize.offset (0, counter * (heightDelta / (numSubviews))); } viewSize.setHeight (viewSize.getHeight () + (heightDelta / (numSubviews))); mouseSize.setHeight (mouseSize.getHeight () + (heightDelta / (numSubviews))); } else if (heightDelta != 0 && autosize & kAutosizeBottom) { viewSize.bottom += heightDelta; mouseSize.bottom += heightDelta; if (!(autosize & kAutosizeTop)) { viewSize.top += heightDelta; mouseSize.top += heightDelta; } } if (viewSize != pV->getViewSize ()) { pV->setViewSize (viewSize); pV->setMouseableArea (mouseSize); } } v = v->next; // counter++; } } CViewContainer::setViewSize (rect, invalid); } } // VSTGUI