diff --git a/.github/workflows/cmake_linux.yml b/.github/workflows/cmake_linux.yml index 297ba3d02..59b79a9ea 100644 --- a/.github/workflows/cmake_linux.yml +++ b/.github/workflows/cmake_linux.yml @@ -11,7 +11,7 @@ jobs: os: [ubuntu-20.04, ubuntu-22.04] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: sudo apt-get update - run: sudo apt-get install libx11-dev libx11-xcb-dev libxcb-util-dev libxcb-cursor-dev libxcb-keysyms1-dev libxcb-xkb-dev libxkbcommon-dev libxkbcommon-x11-dev libfontconfig1-dev libcairo2-dev libfreetype6-dev libpango1.0-dev diff --git a/.github/workflows/cmake_macos.yml b/.github/workflows/cmake_macos.yml index 8cf553ac8..9ea892ce0 100644 --- a/.github/workflows/cmake_macos.yml +++ b/.github/workflows/cmake_macos.yml @@ -8,10 +8,10 @@ jobs: strategy: matrix: - os: [macos-11, macos-12] + os: [macos-11, macos-13] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/actions/cmake with: diff --git a/.github/workflows/cmake_windows.yml b/.github/workflows/cmake_windows.yml index 830117266..dad9c6441 100644 --- a/.github/workflows/cmake_windows.yml +++ b/.github/workflows/cmake_windows.yml @@ -11,7 +11,7 @@ jobs: os: [windows-2019, windows-2022] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/actions/cmake with: diff --git a/vstgui/contrib/datepicker.mm b/vstgui/contrib/datepicker.mm index aa1a4a245..9aa020149 100644 --- a/vstgui/contrib/datepicker.mm +++ b/vstgui/contrib/datepicker.mm @@ -141,6 +141,9 @@ static void validate (id self, SEL cmd, NSDatePickerCell* datePickerCell, dateComponents.month = date.month; dateComponents.year = date.year; impl->view.dateValue = [calendar dateFromComponents:dateComponents]; +#if !__has_feature(objc_arc) + [dateComponents release]; +#endif } //------------------------------------------------------------------------ diff --git a/vstgui/doxygen/page_changes.h b/vstgui/doxygen/page_changes.h index fb35ba5d6..306bd982c 100644 --- a/vstgui/doxygen/page_changes.h +++ b/vstgui/doxygen/page_changes.h @@ -22,6 +22,12 @@ It's recommended to start new projects with version 4 while old projects should @section new_stuff New Stuff +@subsection version4_14 Version 4.14 + +- add crosshair mouse cursor (kCursorCrosshair) +- customizable knob range (see CKnob::setKnobRange) +- new layouts for CRowColumnView + @subsection version4_13 Version 4.13 - support embedding platform views (HWND & NSView) as sub views (see CExternalView and ExternalView::IView) and examples in the contrib folder. @@ -136,6 +142,10 @@ Note: All current deprecated methods will be removed in the next version. So mak @section code_changes Changes for existing VSTGUI code +@subsection code_changes_4_13_to_4_14 VSTGUI 4.13 -> VSTGUI 4.14 + +- In CParamDisplay::drawPlatformText(..) the string argument changed from IPlatformString to UTF8Text + @subsection code_changes_4_12_to_4_13 VSTGUI 4.12 -> VSTGUI 4.13 - the context argument of IFontPainter has changed to use the new platform graphics device context diff --git a/vstgui/lib/controls/cknob.cpp b/vstgui/lib/controls/cknob.cpp index 53f247a5f..074fe5953 100644 --- a/vstgui/lib/controls/cknob.cpp +++ b/vstgui/lib/controls/cknob.cpp @@ -13,9 +13,9 @@ namespace VSTGUI { #if TARGET_OS_IPHONE -static const float kCKnobRange = 300.f; +static const float kCKnobRangeDefault = 300.f; #else -static const float kCKnobRange = 200.f; +static const float kCKnobRangeDefault = 200.f; #endif static constexpr CViewAttributeID kCKnobMouseStateAttribute = 'knms'; @@ -40,6 +40,7 @@ CKnobBase::CKnobBase (const CRect& size, IControlListener* listener, int32_t tag setStartAngle ((float)(3.f * Constants::quarter_pi)); setRangeAngle ((float)(3.f * Constants::half_pi)); zoomFactor = 1.5f; + knobRange = kCKnobRangeDefault; } //------------------------------------------------------------------------ @@ -112,7 +113,7 @@ CMouseEventResult CKnobBase::onMouseDown (CPoint& where, const CButtonState& but mouseState.modeLinear = false; mouseState.entryState = value; - mouseState.range = kCKnobRange; + mouseState.range = knobRange; mouseState.coef = (getMax () - getMin ()) / mouseState.range; mouseState.oldButton = buttons; @@ -193,7 +194,7 @@ CMouseEventResult CKnobBase::onMouseMoved (CPoint& where, const CButtonState& bu CCoord diff = (mouseState.firstPoint.y - where.y) + (where.x - mouseState.firstPoint.x); if (buttons != mouseState.oldButton) { - mouseState.range = kCKnobRange; + mouseState.range = knobRange; if (buttons & kZoomModifier) mouseState.range *= zoomFactor; diff --git a/vstgui/lib/controls/cknob.h b/vstgui/lib/controls/cknob.h index 0f5eb538a..08e5bdf3c 100644 --- a/vstgui/lib/controls/cknob.h +++ b/vstgui/lib/controls/cknob.h @@ -33,6 +33,9 @@ class CKnobBase : public CControl, protected CMouseWheelEditingSupport virtual CCoord getInsetValue () const { return inset; } virtual void setInsetValue (CCoord val) { inset = val; } + + virtual void setKnobRange (float val) { if (val > 0.f) knobRange = val; } + virtual float getKnobRange () const { return knobRange; } //@} // overrides @@ -56,6 +59,7 @@ class CKnobBase : public CControl, protected CMouseWheelEditingSupport float startAngle, rangeAngle; float zoomFactor; + float knobRange; CCoord inset; private: diff --git a/vstgui/lib/controls/coptionmenu.cpp b/vstgui/lib/controls/coptionmenu.cpp index 82c69ba46..9901b1c16 100644 --- a/vstgui/lib/controls/coptionmenu.cpp +++ b/vstgui/lib/controls/coptionmenu.cpp @@ -842,7 +842,7 @@ void COptionMenu::draw (CDrawContext *pContext) CMenuItem* item = getEntry (currentIndex); drawBack (pContext, inPopup ? bgWhenClick : nullptr); if (item) - drawPlatformText (pContext, UTF8String (item->getTitle ()).getPlatformString ()); + drawPlatformText (pContext, item->getTitle ()); setDirty (false); } diff --git a/vstgui/lib/controls/cparamdisplay.cpp b/vstgui/lib/controls/cparamdisplay.cpp index dcfe28941..558e9227a 100644 --- a/vstgui/lib/controls/cparamdisplay.cpp +++ b/vstgui/lib/controls/cparamdisplay.cpp @@ -192,7 +192,7 @@ void CParamDisplay::draw (CDrawContext *pContext) } drawBack (pContext); - drawPlatformText (pContext, UTF8String (string).getPlatformString ()); + drawPlatformText (pContext, UTF8String (string)); setDirty (false); } @@ -322,13 +322,14 @@ void CParamDisplay::drawBack (CDrawContext* pContext, CBitmap* newBack) } //------------------------------------------------------------------------ -void CParamDisplay::drawPlatformText (CDrawContext* pContext, IPlatformString* string) +void CParamDisplay::drawPlatformText (CDrawContext* pContext, const UTF8String& string) { drawPlatformText (pContext, string, getViewSize ()); } //------------------------------------------------------------------------ -void CParamDisplay::drawPlatformText (CDrawContext* pContext, IPlatformString* string, const CRect& size) +void CParamDisplay::drawPlatformText (CDrawContext* pContext, const UTF8String& string, + const CRect& size) { if (!hasBit (style, kNoTextStyle)) { @@ -351,10 +352,12 @@ void CParamDisplay::drawPlatformText (CDrawContext* pContext, IPlatformString* s CRect newSize (textRect); newSize.offset (shadowTextOffset); pContext->setFontColor (shadowColor); - pContext->drawString (string, newSize, horiTxtAlign, hasBit (style, kAntialias)); + pContext->drawString (string.getPlatformString (), newSize, horiTxtAlign, + hasBit (style, kAntialias)); } pContext->setFontColor (fontColor); - pContext->drawString (string, textRect, horiTxtAlign, hasBit (style, kAntialias)); + pContext->drawString (string.getPlatformString (), textRect, horiTxtAlign, + hasBit (style, kAntialias)); }); pContext->restoreGlobalState (); } diff --git a/vstgui/lib/controls/cparamdisplay.h b/vstgui/lib/controls/cparamdisplay.h index bc69ceef1..6aadee583 100644 --- a/vstgui/lib/controls/cparamdisplay.h +++ b/vstgui/lib/controls/cparamdisplay.h @@ -122,8 +122,9 @@ class CParamDisplay : public CControl ~CParamDisplay () noexcept override; virtual void drawBack (CDrawContext* pContext, CBitmap* newBack = nullptr); - virtual void drawPlatformText (CDrawContext* pContext, IPlatformString* string); - virtual void drawPlatformText (CDrawContext* pContext, IPlatformString* string, const CRect& size); + virtual void drawPlatformText (CDrawContext* pContext, const UTF8String& string); + virtual void drawPlatformText (CDrawContext* pContext, const UTF8String& string, + const CRect& size); virtual void drawStyleChanged (); diff --git a/vstgui/lib/controls/csearchtextedit.cpp b/vstgui/lib/controls/csearchtextedit.cpp index 329da775d..84bee20a4 100644 --- a/vstgui/lib/controls/csearchtextedit.cpp +++ b/vstgui/lib/controls/csearchtextedit.cpp @@ -117,10 +117,10 @@ void CSearchTextEdit::draw (CDrawContext *pContext) CColor color (fontColor); color.alpha /= 2; setFontColor (color); - drawPlatformText (pContext, getPlaceholderString ().getPlatformString (), getTextRect ()); + drawPlatformText (pContext, getPlaceholderString (), getTextRect ()); } else - drawPlatformText (pContext, getText ().getPlatformString (), getTextRect ()); + drawPlatformText (pContext, getText (), getTextRect ()); setDirty (false); setFontColor (origFontColor); diff --git a/vstgui/lib/controls/ctextedit.cpp b/vstgui/lib/controls/ctextedit.cpp index bf62350c9..9205c8129 100644 --- a/vstgui/lib/controls/ctextedit.cpp +++ b/vstgui/lib/controls/ctextedit.cpp @@ -188,7 +188,7 @@ void CTextEdit::draw (CDrawContext *pContext) { pContext->saveGlobalState (); pContext->setGlobalAlpha (pContext->getGlobalAlpha () * 0.5f); - drawPlatformText (pContext, placeholderString.getPlatformString ()); + drawPlatformText (pContext, placeholderString); pContext->restoreGlobalState (); } setDirty (false); @@ -201,7 +201,7 @@ void CTextEdit::draw (CDrawContext *pContext) { pContext->saveGlobalState (); pContext->setGlobalAlpha (pContext->getGlobalAlpha () * 0.5f); - drawPlatformText (pContext, placeholderString.getPlatformString ()); + drawPlatformText (pContext, placeholderString); pContext->restoreGlobalState (); } } @@ -211,7 +211,7 @@ void CTextEdit::draw (CDrawContext *pContext) UTF8String str; for (auto i = 0u; i < text.length (); ++i) str += bulletCharacter; - drawPlatformText (pContext, str.getPlatformString ()); + drawPlatformText (pContext, str); } else CTextLabel::draw (pContext); diff --git a/vstgui/lib/controls/ctextlabel.cpp b/vstgui/lib/controls/ctextlabel.cpp index 3e3e29fe6..86264350b 100644 --- a/vstgui/lib/controls/ctextlabel.cpp +++ b/vstgui/lib/controls/ctextlabel.cpp @@ -109,7 +109,7 @@ const UTF8String& CTextLabel::getText () const void CTextLabel::draw (CDrawContext *pContext) { drawBack (pContext); - drawPlatformText (pContext, truncatedText.empty () ? text.getPlatformString () : truncatedText.getPlatformString ()); + drawPlatformText (pContext, truncatedText.empty () ? text : truncatedText); setDirty (false); } diff --git a/vstgui/lib/crowcolumnview.cpp b/vstgui/lib/crowcolumnview.cpp index 90eda08b2..a795892f6 100644 --- a/vstgui/lib/crowcolumnview.cpp +++ b/vstgui/lib/crowcolumnview.cpp @@ -5,8 +5,229 @@ #include "crowcolumnview.h" #include "animation/animations.h" #include "animation/timingfunctions.h" +#include namespace VSTGUI { +namespace Layouting { +//-------------------------------------------------------------------------------- +using CRects = std::vector; + +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) @@ -127,6 +348,10 @@ void CRowColumnView::layoutViewsEqualSize () 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 (); @@ -154,6 +379,19 @@ void CRowColumnView::layoutViewsEqualSize () 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; } diff --git a/vstgui/lib/crowcolumnview.h b/vstgui/lib/crowcolumnview.h index 0eee783be..8658cb299 100644 --- a/vstgui/lib/crowcolumnview.h +++ b/vstgui/lib/crowcolumnview.h @@ -54,7 +54,18 @@ class CRowColumnView : public CAutoLayoutContainerView /** subviews have the same right or bottom position */ kRightBottomEqualy, /** stretch subviews to the same width and height */ - kStretchEqualy + kStretchEqualy, + + /** options for the auto layout feature */ + kTopLeft, + kTopCenter, + kTopRight, + kMiddleLeft, + kMiddleCenter, + kMiddleRight, + kBottomLeft, + kBottomCenter, + kBottomRight }; CRowColumnView (const CRect& size, Style style = kRowStyle, LayoutStyle layoutStyle = kLeftTopEqualy, CCoord spacing = 0., const CRect& margin = CRect (0., 0., 0., 0.)); diff --git a/vstgui/lib/finally.h b/vstgui/lib/finally.h index f2ad292f7..b795e3059 100644 --- a/vstgui/lib/finally.h +++ b/vstgui/lib/finally.h @@ -18,7 +18,7 @@ class FinalAction FinalAction (FinalAction&& other) noexcept { action = std::move (other.action); - other.invoke (false); + other.invoke = false; } FinalAction (const FinalAction&) = delete; FinalAction& operator= (const FinalAction&) = delete; diff --git a/vstgui/lib/platform/common/generictextedit.cpp b/vstgui/lib/platform/common/generictextedit.cpp index f6725b971..393e76e9d 100644 --- a/vstgui/lib/platform/common/generictextedit.cpp +++ b/vstgui/lib/platform/common/generictextedit.cpp @@ -687,7 +687,7 @@ void STBTextEditView::draw (CDrawContext* context) calcCursorSizes (); drawBack (context, nullptr); - drawPlatformText (context, getText ().getPlatformString ()); + drawPlatformText (context, getText ()); if (!isBlinkToggle () || editState.select_start != editState.select_end) return; diff --git a/vstgui/lib/platform/linux/cairogradient.cpp b/vstgui/lib/platform/linux/cairogradient.cpp index 9488d9916..296141690 100644 --- a/vstgui/lib/platform/linux/cairogradient.cpp +++ b/vstgui/lib/platform/linux/cairogradient.cpp @@ -44,22 +44,24 @@ const PatternHandle& Gradient::getLinearGradient (CPoint start, CPoint end) cons } //------------------------------------------------------------------------ -const PatternHandle& Gradient::getRadialGradient () +const PatternHandle& Gradient::getRadialGradient (CPoint center, CCoord radius, + CPoint originOffset) const { if (!radialGradient) { - radialGradient = PatternHandle (cairo_pattern_create_radial (0, 0, 1, 0, 0, 1)); + radialGradient = PatternHandle ( + cairo_pattern_create_radial (center.x, center.y, 0., center.x, center.y, radius)); + for (auto& it : getColorStops ()) { cairo_pattern_add_color_stop_rgba ( - radialGradient, it.first, it.second.normRed (), - it.second.normGreen (), it.second.normBlue (), - it.second.normAlpha ()); + radialGradient, it.first, it.second.normRed (), + it.second.normGreen (), it.second.normBlue (), + it.second.normAlpha ()); } } return radialGradient; } - //------------------------------------------------------------------------ } // Cairo } // VSTGUI diff --git a/vstgui/lib/platform/linux/cairogradient.h b/vstgui/lib/platform/linux/cairogradient.h index 785247db9..e0eb8453c 100644 --- a/vstgui/lib/platform/linux/cairogradient.h +++ b/vstgui/lib/platform/linux/cairogradient.h @@ -20,7 +20,8 @@ class Gradient : public PlatformGradientBase ~Gradient () noexcept override; const PatternHandle& getLinearGradient (CPoint start, CPoint end) const; - const PatternHandle& getRadialGradient (); + const PatternHandle& getRadialGradient (CPoint center, CCoord radius, + CPoint originOffset) const; private: void changed () override; diff --git a/vstgui/lib/platform/linux/cairographicscontext.cpp b/vstgui/lib/platform/linux/cairographicscontext.cpp index ab620cc91..ed7e493fd 100644 --- a/vstgui/lib/platform/linux/cairographicscontext.cpp +++ b/vstgui/lib/platform/linux/cairographicscontext.cpp @@ -624,8 +624,33 @@ bool CairoGraphicsDeviceContext::fillRadialGradient (IPlatformGraphicsPath& path auto cairoPath = dynamic_cast (&path); if (!cairoPath) return false; - // TODO: Implementation - return false; + + auto cairoGradient = dynamic_cast (&gradient); + if (!cairoGradient) + return false; + impl->doInContext ([&] () { + std::unique_ptr alignedPath; + if (impl->state.drawMode.integralMode ()) + { + alignedPath = cairoPath->copyPixelAlign ([&] (CPoint p) { + p = pixelAlign (impl->state.tm, p); + return p; + }); + } + auto p = alignedPath ? alignedPath->getCairoPath () : cairoPath->getCairoPath (); + cairo_append_path (impl->context, p); + + const auto& radialGradient = + cairoGradient->getRadialGradient (center, radius, originOffset); + cairo_set_source (impl->context, radialGradient); + if (evenOdd) + cairo_set_fill_rule (impl->context, CAIRO_FILL_RULE_EVEN_ODD); + + cairo_arc (impl->context, 0, 0, 0, 0., M_PI * 2.); + cairo_fill (impl->context); + }); + + return true; } //------------------------------------------------------------------------ diff --git a/vstgui/lib/platform/linux/x11platform.cpp b/vstgui/lib/platform/linux/x11platform.cpp index d429e4bea..f09f615bd 100644 --- a/vstgui/lib/platform/linux/x11platform.cpp +++ b/vstgui/lib/platform/linux/x11platform.cpp @@ -511,6 +511,7 @@ uint32_t RunLoop::getCursorID (CCursorType cursor) case kCursorNWSESize: cursorID = makeCursor (impl->cursorContext, CursorNWSESizeNames); break; + case kCursorCrosshair: case kCursorSizeAll: cursorID = makeCursor (impl->cursorContext, CursorSizeAllNames); break; diff --git a/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm b/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm index 8905a992a..25193bd21 100644 --- a/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm +++ b/vstgui/lib/platform/mac/cocoa/cocoahelpers.mm @@ -137,7 +137,7 @@ HIDDEN bool CreateKeyboardEventFromNSEvent (NSEvent* theEvent, KeyboardEvent& ev unichar character = 0; switch (virtualKey) { - case VirtualKey::Back: character = NSDeleteCharacter; break; + case VirtualKey::Back: character = NSBackspaceCharacter; break; case VirtualKey::Tab: character = NSTabCharacter; break; case VirtualKey::Clear: character = NSClearLineFunctionKey; break; case VirtualKey::Return: character = NSCarriageReturnCharacter; break; diff --git a/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm b/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm index 6ab7ab439..3aa4fff2a 100644 --- a/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm +++ b/vstgui/lib/platform/mac/cocoa/nsviewdraggingsession.mm @@ -252,7 +252,7 @@ static Class CreateClass () //----------------------------------------------------------------------------- NSImage* NSViewDraggingSession::nsImageForDragOperation (CBitmap* bitmap) { - return bitmapToNSImage (bitmap); + return [bitmapToNSImage (bitmap) autorelease]; } //------------------------------------------------------------------------ diff --git a/vstgui/lib/platform/mac/cocoa/nsviewframe.mm b/vstgui/lib/platform/mac/cocoa/nsviewframe.mm index d08413026..34a3bd3e9 100644 --- a/vstgui/lib/platform/mac/cocoa/nsviewframe.mm +++ b/vstgui/lib/platform/mac/cocoa/nsviewframe.mm @@ -1333,6 +1333,7 @@ static MouseEventButtonState buttonStateFromNSEvent (NSEvent* theEvent) case kCursorNotAllowed: cur = [NSCursor performSelector:@selector(operationNotAllowedCursor)]; break; case kCursorHand: cur = [NSCursor openHandCursor]; break; case kCursorIBeam: cur = [NSCursor IBeamCursor]; break; + case kCursorCrosshair: cur = [NSCursor crosshairCursor]; break; default: cur = [NSCursor arrowCursor]; break; } if (cur) diff --git a/vstgui/lib/platform/win32/direct2d/d2d.h b/vstgui/lib/platform/win32/direct2d/d2d.h index b8514340a..7ca4a0005 100644 --- a/vstgui/lib/platform/win32/direct2d/d2d.h +++ b/vstgui/lib/platform/win32/direct2d/d2d.h @@ -73,6 +73,25 @@ inline TransformMatrix convert (const D2D1_MATRIX_3X2_F& t) return matrix; } +//------------------------------------------------------------------------ +inline D2D1_BITMAP_INTERPOLATION_MODE convert (BitmapInterpolationQuality quality) +{ + D2D1_BITMAP_INTERPOLATION_MODE mode; + switch (quality) + { + case BitmapInterpolationQuality::kLow: + mode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + break; + + case BitmapInterpolationQuality::kMedium: + case BitmapInterpolationQuality::kHigh: + default: + mode = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; + break; + } + return mode; +} + //------------------------------------------------------------------------ } // anonymous namespace diff --git a/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.cpp b/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.cpp index 67e0093d1..107e33c1e 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.cpp +++ b/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.cpp @@ -12,10 +12,12 @@ #include "../comptr.h" #include "../../../crect.h" #include "../../../cgraphicstransform.h" +#include "../../../cbitmap.h" #include "../../../ccolor.h" #include "../../../cdrawdefs.h" #include "../../../clinestyle.h" #include +#include #include #include @@ -125,6 +127,46 @@ struct D2DBitmapDeviceContext : D2DGraphicsDeviceContext SharedPointer bitmap; }; +//------------------------------------------------------------------------ +COM::Ptr createBrushFromBitmap (ID2D1DeviceContext* deviceContext, + ID2D1Bitmap* d2d1Bitmap, CRect srcRect, + CRect dstRect, double alpha, + BitmapInterpolationQuality quality, + CGraphicsTransform* brushTM = nullptr) +{ + CGraphicsTransform brushTransform; + if (brushTM) + brushTransform = *brushTM; + brushTransform.translate (dstRect.getTopLeft ()); + + D2D1_IMAGE_BRUSH_PROPERTIES imageBrushProp = {}; + imageBrushProp.sourceRectangle = convert (srcRect); + imageBrushProp.extendModeX = imageBrushProp.extendModeY = D2D1_EXTEND_MODE_WRAP; + switch (quality) + { + case BitmapInterpolationQuality::kLow: + imageBrushProp.interpolationMode = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + break; + + case BitmapInterpolationQuality::kMedium: + case BitmapInterpolationQuality::kHigh: + default: + imageBrushProp.interpolationMode = D2D1_INTERPOLATION_MODE_LINEAR; + break; + } + + D2D1_BRUSH_PROPERTIES brushProp = {}; + brushProp.opacity = static_cast (alpha); + brushProp.transform = convert (brushTransform); + + COM::Ptr brush; + auto hr = + deviceContext->CreateImageBrush (d2d1Bitmap, imageBrushProp, brushProp, brush.adoptPtr ()); + if (FAILED (hr)) + return {}; + return brush; +} + //------------------------------------------------------------------------ } // anonymous namespace @@ -643,24 +685,10 @@ bool D2DGraphicsDeviceContext::drawBitmap (IPlatformBitmap& bitmap, CRect dest, source.setWidth (d2d1Bitmap->GetSize ().width); source.setHeight (d2d1Bitmap->GetSize ().height); - D2D1_BITMAP_INTERPOLATION_MODE mode; - switch (quality) - { - case BitmapInterpolationQuality::kLow: - mode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; - break; - - case BitmapInterpolationQuality::kMedium: - case BitmapInterpolationQuality::kHigh: - default: - mode = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; - break; - } - D2D1_RECT_F sourceRect = convert (source); deviceContext->DrawBitmap (d2d1Bitmap, convert (d), - static_cast (alpha * impl->state.globalAlpha), mode, - &sourceRect); + static_cast (alpha * impl->state.globalAlpha), + convert (quality), &sourceRect); }); setTransformMatrix (originalTransformMatrix); impl->state.clip = originalClip; @@ -899,12 +927,88 @@ const IPlatformGraphicsDeviceContextBitmapExt* D2DGraphicsDeviceContext::asBitma } //------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawBitmapNinePartTiled (IPlatformBitmap& bitmap, CRect dest, +bool D2DGraphicsDeviceContext::drawBitmapNinePartTiled (IPlatformBitmap& bitmap, CRect dstRect, const CNinePartTiledDescription& desc, double alpha, BitmapInterpolationQuality quality) const { - return false; + using NPTD = CNinePartTiledDescription; + + alpha *= impl->state.globalAlpha; + if (alpha == 0.) + return true; + + D2DBitmap* d2dBitmap = dynamic_cast (&bitmap); + if (!d2dBitmap || !d2dBitmap->getSource ()) + return false; + auto d2d1Bitmap = + D2DBitmapCache::getBitmap (d2dBitmap, impl->deviceContext.get (), impl->device.get ()); + if (!d2d1Bitmap) + return false; + + auto interpolationMode = convert (quality); + auto originalClip = impl->state.clip; + auto cr = dstRect; + impl->state.tm.transform (cr); + cr.bound (originalClip); + impl->state.clip = cr; + + auto originalTransformMatrix = impl->state.tm; + + double bitmapScaleFactor = d2dBitmap->getScaleFactor (); + CGraphicsTransform bitmapTransform; + bitmapTransform.scale (bitmapScaleFactor, bitmapScaleFactor); + auto invBitmapTransform = bitmapTransform.inverse (); + setTransformMatrix ({}); + + CRect bitmapBounds (0, 0, bitmap.getSize ().x, bitmap.getSize ().y); + invBitmapTransform.transform (bitmapBounds); + + std::array mySourceRect; + std::array myDestRect; + + desc.calcRects (bitmapBounds, mySourceRect.data ()); + desc.calcRects (dstRect, myDestRect.data ()); + + std::for_each (mySourceRect.begin (), mySourceRect.end (), + [&] (auto& el) { bitmapTransform.transform (el); }); + std::for_each (myDestRect.begin (), myDestRect.end (), + [&] (auto& el) { originalTransformMatrix.transform (el); }); + + impl->doInContext ([&] (auto deviceContext) { + auto drawBitmap = [&] (const CRect& source, const CRect& destination) { + auto sourceRect = convert (source); + deviceContext->DrawBitmap (d2d1Bitmap, convert (destination), + static_cast (alpha), interpolationMode, &sourceRect); + }; + + drawBitmap (mySourceRect[NPTD::kPartTopLeft], myDestRect[NPTD::kPartTopLeft]); + drawBitmap (mySourceRect[NPTD::kPartTopRight], myDestRect[NPTD::kPartTopRight]); + drawBitmap (mySourceRect[NPTD::kPartBottomLeft], myDestRect[NPTD::kPartBottomLeft]); + drawBitmap (mySourceRect[NPTD::kPartBottomRight], myDestRect[NPTD::kPartBottomRight]); + + auto tm = originalTransformMatrix * invBitmapTransform; + tm.dx = tm.dy = 0; + + auto drawBitmapBrush = [&] (CRect src, CRect dst) { + if (auto brush = createBrushFromBitmap (deviceContext, d2d1Bitmap, src, dst, alpha, + quality, &tm)) + { + dst.makeIntegral (); + deviceContext->FillRectangle (convert (dst), brush.get ()); + } + }; + + drawBitmapBrush (mySourceRect[NPTD::kPartTop], myDestRect[NPTD::kPartTop]); + drawBitmapBrush (mySourceRect[NPTD::kPartCenter], myDestRect[NPTD::kPartCenter]); + drawBitmapBrush (mySourceRect[NPTD::kPartLeft], myDestRect[NPTD::kPartLeft]); + drawBitmapBrush (mySourceRect[NPTD::kPartRight], myDestRect[NPTD::kPartRight]); + drawBitmapBrush (mySourceRect[NPTD::kPartBottom], myDestRect[NPTD::kPartBottom]); + }); + + setTransformMatrix (originalTransformMatrix); + impl->state.clip = originalClip; + return true; } //------------------------------------------------------------------------ @@ -932,32 +1036,9 @@ bool D2DGraphicsDeviceContext::fillRectWithBitmap (IPlatformBitmap& bitmap, CRec invBitmapTransform.transform (dstRect); invBitmapTransform.transform (srcRect); - D2D1_IMAGE_BRUSH_PROPERTIES imageBrushProp = {}; - imageBrushProp.sourceRectangle = convert (srcRect); - imageBrushProp.extendModeX = imageBrushProp.extendModeY = D2D1_EXTEND_MODE_WRAP; - switch (quality) - { - case BitmapInterpolationQuality::kLow: - imageBrushProp.interpolationMode = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR; - break; - - case BitmapInterpolationQuality::kMedium: - case BitmapInterpolationQuality::kHigh: - default: - imageBrushProp.interpolationMode = D2D1_INTERPOLATION_MODE_LINEAR; - break; - } - CGraphicsTransform brushTransform; - brushTransform.translate (dstRect.getTopLeft ()); - - D2D1_BRUSH_PROPERTIES brushProp = {}; - brushProp.opacity = 1.f; - brushProp.transform = convert (brushTransform); - - COM::Ptr brush; - auto hr = impl->deviceContext->CreateImageBrush (d2d1Bitmap, imageBrushProp, brushProp, - brush.adoptPtr ()); - if (FAILED (hr)) + auto brush = createBrushFromBitmap (impl->deviceContext.get (), d2d1Bitmap, srcRect, dstRect, + alpha * impl->state.globalAlpha, quality); + if (!brush) return false; impl->state.clip = cr; diff --git a/vstgui/lib/platform/win32/win32directcomposition.cpp b/vstgui/lib/platform/win32/win32directcomposition.cpp index cef864410..8567c8257 100644 --- a/vstgui/lib/platform/win32/win32directcomposition.cpp +++ b/vstgui/lib/platform/win32/win32directcomposition.cpp @@ -678,6 +678,8 @@ bool Factory::Impl::createD3D11Device () D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1}; UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + // fix UI flickering with some GPU cards/drivers + creationFlags |= D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS; #if DEBUG creationFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif diff --git a/vstgui/lib/platform/win32/win32dll.h b/vstgui/lib/platform/win32/win32dll.h index 3824aa1d8..0206316c0 100644 --- a/vstgui/lib/platform/win32/win32dll.h +++ b/vstgui/lib/platform/win32/win32dll.h @@ -123,6 +123,14 @@ struct HiDPISupport : DllBase return false; } + bool adjustWindowRectExForDpi (LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, + UINT dpi) const + { + if (!adjustWindowRectExForDpiProc) + return false; + return adjustWindowRectExForDpiProc (lpRect, dwStyle, bMenu, dwExStyle, dpi); + } + private: using GetDpiForWindowFunc = UINT (WINAPI*) (HWND hWnd); using GetDpiForMonitorFunc = HRESULT (WINAPI*) (_In_ HMONITOR hmonitor, @@ -132,12 +140,15 @@ struct HiDPISupport : DllBase using SetProcessDpiAwarnessFunc = HRESULT (WINAPI*) (_In_ PROCESS_DPI_AWARENESS value); using EnableNonClientDpiScalingFunc = BOOL (WINAPI*) (_In_ HWND hwnd); using SetProcessDpiAwarenessContextFunc = BOOL (WINAPI*) (_In_ DPI_AWARENESS_CONTEXT value); + using AdjustWindowRectExForDpiProc = BOOL (WINAPI*) (LPRECT, DWORD, BOOL, DWORD, UINT); GetDpiForWindowFunc getDPIForWindowFunc {nullptr}; GetDpiForMonitorFunc getDpiForMonitorFunc {nullptr}; SetProcessDpiAwarnessFunc setProcessDpiAwarenessFunc {nullptr}; EnableNonClientDpiScalingFunc enableNonClientDpiScalingFunc {nullptr}; SetProcessDpiAwarenessContextFunc setProcessDpiAwarenessContextFunc {nullptr}; + AdjustWindowRectExForDpiProc adjustWindowRectExForDpiProc {nullptr}; + DllBase shCore {"Shcore.dll"}; HiDPISupport () : DllBase ("User32.dll") @@ -149,6 +160,8 @@ struct HiDPISupport : DllBase setProcessDpiAwarenessFunc = shCore.getProcAddress ("SetProcessDpiAwareness"); setProcessDpiAwarenessContextFunc = getProcAddress ("SetProcessDpiAwarenessContext"); + adjustWindowRectExForDpiProc = + getProcAddress ("AdjustWindowRectExForDpi"); } }; diff --git a/vstgui/lib/platform/win32/win32frame.cpp b/vstgui/lib/platform/win32/win32frame.cpp index 886100832..1d47515da 100644 --- a/vstgui/lib/platform/win32/win32frame.cpp +++ b/vstgui/lib/platform/win32/win32frame.cpp @@ -384,6 +384,9 @@ bool Win32Frame::setMouseCursor (CCursorType type) case kCursorHand: cursor = LoadCursor (nullptr, IDC_HAND); break; + case kCursorCrosshair: + cursor = LoadCursor (nullptr, IDC_CROSS); + break; default: cursor = LoadCursor (nullptr, IDC_ARROW); break; @@ -661,6 +664,7 @@ void Win32Frame::paint (HWND hwnd) *static_cast (graphicsDevice.get ()), deviceContext, tm); + drawDevice->clearRect (rect); getFrame ()->platformDrawRects (drawDevice, 1., {1, rect}); }); }); diff --git a/vstgui/lib/vstguibase.h b/vstgui/lib/vstguibase.h index 92ef90975..2398067b9 100644 --- a/vstgui/lib/vstguibase.h +++ b/vstgui/lib/vstguibase.h @@ -7,13 +7,14 @@ #include #include #include +#include //----------------------------------------------------------------------------- // VSTGUI Version //----------------------------------------------------------------------------- #define VSTGUI_VERSION_MAJOR 4 -#define VSTGUI_VERSION_MINOR 13 -#define VSTGUI_VERSION_PATCHLEVEL 3 +#define VSTGUI_VERSION_MINOR 14 +#define VSTGUI_VERSION_PATCHLEVEL 0 //----------------------------------------------------------------------------- // Platform definitions @@ -545,6 +546,7 @@ struct BitScopeToggleT #define VSTGUI_NEWER_THAN_4_10 VSTGUI_NEWER_THAN (4, 10) #define VSTGUI_NEWER_THAN_4_11 VSTGUI_NEWER_THAN (4, 11) #define VSTGUI_NEWER_THAN_4_12 VSTGUI_NEWER_THAN (4, 12) +#define VSTGUI_NEWER_THAN_4_13 VSTGUI_NEWER_THAN (4, 13) } // VSTGUI diff --git a/vstgui/lib/vstguifwd.h b/vstgui/lib/vstguifwd.h index f68c25968..c8a1d43ed 100644 --- a/vstgui/lib/vstguifwd.h +++ b/vstgui/lib/vstguifwd.h @@ -70,6 +70,8 @@ enum CCursorType kCursorHand, /** i beam cursor */ kCursorIBeam, + /** crosshair cursor */ + kCursorCrosshair, }; //---------------------------- diff --git a/vstgui/plugin-bindings/vst3editor.cpp b/vstgui/plugin-bindings/vst3editor.cpp index 0f9de0cdf..886b8bc84 100644 --- a/vstgui/plugin-bindings/vst3editor.cpp +++ b/vstgui/plugin-bindings/vst3editor.cpp @@ -561,9 +561,15 @@ bool VST3Editor::setEditorSizeConstrains (const CPoint& newMinimumSize, const CP //----------------------------------------------------------------------------- double VST3Editor::getAbsScaleFactor () const { - return zoomFactor * contentScaleFactor; + return getZoomFactor () * getContentScaleFactor (); } +//----------------------------------------------------------------------------- +double VST3Editor::getContentScaleFactor () const { return contentScaleFactor; } + +//----------------------------------------------------------------------------- +double VST3Editor::getZoomFactor () const { return zoomFactor; } + #ifdef VST3_CONTENT_SCALE_SUPPORT //----------------------------------------------------------------------------- Steinberg::tresult PLUGIN_API VST3Editor::setContentScaleFactor (ScaleFactor factor) @@ -580,7 +586,7 @@ Steinberg::tresult PLUGIN_API VST3Editor::setContentScaleFactor (ScaleFactor fac //----------------------------------------------------------------------------- void VST3Editor::setZoomFactor (double factor) { - if (zoomFactor == factor) + if (getZoomFactor () == factor) return; zoomFactor = factor; @@ -861,7 +867,7 @@ void VST3Editor::onMouseEvent (MouseEvent& event, CFrame* frame) static_cast ((*it) * 100)); CMenuItem* item = zoomMenu->addEntry (new CCommandMenuItem ( {zoomFactorString, zoomFactorTag, this, "Zoom", zoomFactorString})); - if (zoomFactor == *it) + if (getZoomFactor () == *it) item->setChecked (true); } CMenuItem* item = controllerMenu->addEntry ("UI Zoom"); @@ -1072,6 +1078,9 @@ void VST3Editor::requestRecreateView () } } +//----------------------------------------------------------------------------- +bool VST3Editor::inEditMode () const { return editingEnabled; } + #if LINUX // Map Steinberg Vst Interface to VSTGUI Interface class RunLoop : public X11::IRunLoop, public AtomicReferenceCounted @@ -1242,6 +1251,7 @@ bool PLUGIN_API VST3Editor::open (void* parent, const PlatformType& type) if (!enableEditing (false)) { getFrame ()->forget (); + frame = nullptr; return false; } @@ -1534,7 +1544,30 @@ void VST3Editor::save (bool saveAs) } if (savePath.empty ()) return; - if (description->save (savePath.c_str (), VST3EditorInternal::getUIDescriptionSaveOptions (frame))) + + // filter out attributes we will always override with the values from the parameters + auto filter = [] (CView* view, const std::string& name) -> bool { + if (auto control = dynamic_cast (view)) + { + if (control->getTag () != -1) + { + if (name == UIViewCreator::kAttrMinValue) + return false; + if (name == UIViewCreator::kAttrMaxValue) + return false; + if (name == UIViewCreator::kAttrDefaultValue) + return false; + if (name == UIViewCreator::kAttrMouseEnabled) + return false; + if (name == UIViewCreator::kAttrTitle && dynamic_cast (control)) + return false; + } + } + return true; + }; + + if (description->save (savePath.c_str (), + VST3EditorInternal::getUIDescriptionSaveOptions (frame), filter)) description->setFilePath (savePath.c_str ()); } @@ -1868,7 +1901,7 @@ bool VST3Editor::enableEditing (bool state) getFrame ()->setSize (width, height); getFrame ()->addView (view); - getFrame()->setZoom (contentScaleFactor); + getFrame ()->setZoom (getContentScaleFactor ()); getFrame ()->enableTooltips (true); CColor focusColor = kBlueCColor; @@ -2023,5 +2056,87 @@ UIDescription* VST3Editor::getUIDescription () const return description; } +//------------------------------------------------------------------------ +//--- AspectRatioVST3Editor +//------------------------------------------------------------------------ +void AspectRatioVST3Editor::setMinZoomFactor (double factor) { minZoomFactor = factor; } + +//------------------------------------------------------------------------ +double AspectRatioVST3Editor::getMinZoomFactor () const { return minZoomFactor; } + +//------------------------------------------------------------------------ +bool PLUGIN_API AspectRatioVST3Editor::open (void* parent, const PlatformType& type) +{ + calcZoomFactor = getZoomFactor (); + if (VST3Editor::open (parent, type) && getFrame ()) + { + initialSize = getFrame ()->getViewSize ().getSize (); + initialSize /= calcZoomFactor; + return true; + } + return false; +} + +//------------------------------------------------------------------------ +Steinberg::tresult PLUGIN_API AspectRatioVST3Editor::onSize (Steinberg::ViewRect* newSize) +{ + if (newSize == nullptr) + return Steinberg::kInvalidArgument; + + if (!canCalculateAspectRatio ()) + return VST3Editor::onSize (newSize); + + setZoomFactor (calcZoomFactor); + return Steinberg::kResultTrue; +} + +//------------------------------------------------------------------------ +Steinberg::tresult PLUGIN_API AspectRatioVST3Editor::checkSizeConstraint (Steinberg::ViewRect* rect) +{ + if (rect == nullptr) + return Steinberg::kInvalidArgument; + + if (!canCalculateAspectRatio ()) + return VST3Editor::checkSizeConstraint (rect); + + const CPoint size (rect->getWidth (), rect->getHeight ()); + auto diff = size - initialSize; + auto anchor = initialSize.x >= initialSize.y ? initialSize.x : initialSize.y; + auto sizeAnchor = initialSize.x >= initialSize.y ? size.x : size.y; + + auto factor = sizeAnchor / anchor; + if (factor < minZoomFactor * getContentScaleFactor ()) + factor = minZoomFactor * getContentScaleFactor (); + factor = std::round (factor * anchor) / anchor; + auto newSize = initialSize * factor; + calcZoomFactor = factor / getContentScaleFactor (); + if (newSize != size) + { + rect->right = rect->left + newSize.x; + rect->bottom = rect->top + newSize.y; + } + return Steinberg::kResultTrue; +} + +#ifdef VST3_CONTENT_SCALE_SUPPORT +//------------------------------------------------------------------------ +Steinberg::tresult PLUGIN_API AspectRatioVST3Editor::setContentScaleFactor (ScaleFactor factor) +{ + auto res = VST3Editor::setContentScaleFactor (factor); + if (res == Steinberg::kResultTrue && canCalculateAspectRatio ()) + setZoomFactor (calcZoomFactor); + return res; +} +#endif + +//------------------------------------------------------------------------ +bool AspectRatioVST3Editor::canCalculateAspectRatio () const +{ + auto f = getFrame (); + if (inEditMode () || f == nullptr || f->hasChildren () == false) + return false; + return true; +} + //------------------------------------------------------------------------ } // VSTGUI diff --git a/vstgui/plugin-bindings/vst3editor.h b/vstgui/plugin-bindings/vst3editor.h index b653a8aad..2eea845ef 100644 --- a/vstgui/plugin-bindings/vst3editor.h +++ b/vstgui/plugin-bindings/vst3editor.h @@ -125,8 +125,8 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, bool requestResize (const CPoint& newSize); void setZoomFactor (double factor); - double getZoomFactor () const { return zoomFactor; } - + double getZoomFactor () const; + void setAllowedZoomFactors (std::vector zoomFactors) { allowedZoomFactors = zoomFactors; } /** set the delegate of the editor. no reference counting is happening here. */ @@ -141,6 +141,7 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, ~VST3Editor () override; void init (); double getAbsScaleFactor () const; + double getContentScaleFactor () const; ParameterChangeListener* getParameterChangeListener (int32_t tag) const; void recreateView (); void requestRecreateView (); @@ -152,6 +153,7 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, bool enableShowEditButton () const; void enableShowEditButton (bool state); void showEditButton (bool state); + bool inEditMode () const; bool PLUGIN_API open (void* parent, const PlatformType& type) override; void PLUGIN_API close () override; @@ -221,5 +223,33 @@ class VST3Editor : public Steinberg::Vst::VSTGUIEditor, Optional sizeRequest; }; +//----------------------------------------------------------------------------- +//! @brief An extended VST3 Editor which scales its contents when resized +//! @ingroup new_in_4_14 +//----------------------------------------------------------------------------- +class AspectRatioVST3Editor : public VST3Editor +{ +public: + using VST3Editor::VST3Editor; + + void setMinZoomFactor (double factor); + double getMinZoomFactor () const; + +protected: + bool canCalculateAspectRatio () const; + + bool PLUGIN_API open (void* parent, const PlatformType& type) override; + Steinberg::tresult PLUGIN_API onSize (Steinberg::ViewRect* newSize) override; + Steinberg::tresult PLUGIN_API checkSizeConstraint (Steinberg::ViewRect* rect) override; +#ifdef VST3_CONTENT_SCALE_SUPPORT + Steinberg::tresult PLUGIN_API setContentScaleFactor (ScaleFactor factor) override; +#endif + +private: + CPoint initialSize {}; + double minZoomFactor {1.}; + double calcZoomFactor {1.}; +}; + //------------------------------------------------------------------------ } // VSTGUI diff --git a/vstgui/standalone/examples/standalone/resource/test.uidesc b/vstgui/standalone/examples/standalone/resource/test.uidesc index e31b0a38c..fe3b7cd71 100644 --- a/vstgui/standalone/examples/standalone/resource/test.uidesc +++ b/vstgui/standalone/examples/standalone/resource/test.uidesc @@ -41,7 +41,7 @@ "SelectedRow": "4" }, "UIDescFilePath": { - "path": "/Users/scheffle/git/vstgui/vstgui/standalone/examples/standalone/resource/test.uidesc" + "path": "/Users/scheffle/Source/vstgui/vstgui/standalone/examples/standalone/resource/test.uidesc" }, "UITagsDataSource": { "SelectedRow": "-1" @@ -139,14 +139,10 @@ "checkmark-color": "~ WhiteCColor", "class": "CCheckBox", "control-tag": "Activate", - "default-value": "0.5", "draw-crossbox": "true", "font": "~ SystemFont", "font-color": "~ WhiteCColor", "frame-width": "1", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "20, 10", "round-rect-radius": "2", @@ -163,7 +159,6 @@ "bitmap-offset": "0, 0", "class": "CSlider", "control-tag": "Test", - "default-value": "0.5", "draw-back": "true", "draw-back-color": "~ WhiteCColor", "draw-frame": "true", @@ -174,10 +169,7 @@ "draw-value-inverted": "true", "frame-width": "1", "handle-offset": "0, 0", - "max-value": "1", - "min-value": "0", "mode": "free click", - "mouse-enabled": "true", "opacity": "1", "orientation": "vertical", "origin": "10, 50", @@ -196,16 +188,12 @@ "back-color-selected": "TextShadow", "class": "CStringListControl", "control-tag": "Weekdays", - "default-value": "0.5", "font": "~ NormalFont", "font-color": "~ BlackCColor", "font-color-selected": "~ BlackCColor", "hover-color": "Hover", "line-color": "~ BlackCColor", "line-width": "-1", - "max-value": "7", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "15, 200", "row-height": "16", @@ -227,16 +215,12 @@ "background-offset": "0, 0", "class": "CTextEdit", "control-tag": "MutableString", - "default-value": "0.5", "font": "~ NormalFont", "font-antialias": "true", "font-color": "~ WhiteCColor", "frame-color": "~ WhiteCColor", "frame-width": "1", "immediate-text-change": "true", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "40, 50", "round-rect-radius": "3", @@ -255,7 +239,6 @@ "text-inset": "0, 0", "text-rotation": "0", "text-shadow-offset": "1, 1", - "title": "This is a string value", "tooltip": "Text Edit", "transparent": "false", "value-precision": "2", @@ -269,7 +252,6 @@ "bitmap-offset": "0, 0", "class": "CSlider", "control-tag": "Test", - "default-value": "0.5", "draw-back": "true", "draw-back-color": "~ WhiteCColor", "draw-frame": "true", @@ -280,10 +262,7 @@ "draw-value-inverted": "false", "frame-width": "1", "handle-offset": "0, 0", - "max-value": "1", - "min-value": "0", "mode": "free click", - "mouse-enabled": "true", "opacity": "1", "orientation": "horizontal", "origin": "40, 80", @@ -301,7 +280,6 @@ "autosize": "right top ", "class": "CTextButton", "control-tag": "ShowPopup", - "default-value": "0.5", "font": "~ SystemFont", "frame-color": "~ BlackCColor", "frame-color-highlighted": "~ BlackCColor", @@ -311,9 +289,6 @@ "icon-position": "left", "icon-text-margin": "0", "kick-style": "false", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "280, 80", "round-radius": "10", @@ -333,16 +308,12 @@ "autosize": "left right top ", "class": "CSegmentButton", "control-tag": "StepTest", - "default-value": "0.5", "font": "~ NormalFont", "frame-color": "~ BlackCColor", "frame-width": "1", "gradient": "Default TextButton Gradient", "gradient-highlighted": "Default TextButton Gradient Highlighted", "icon-text-margin": "0", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "40, 110", "round-radius": "3", @@ -365,7 +336,6 @@ "bitmap-offset": "0, 0", "class": "CSlider", "control-tag": "StepTest", - "default-value": "0.5", "draw-back": "true", "draw-back-color": "~ WhiteCColor", "draw-frame": "true", @@ -376,10 +346,7 @@ "draw-value-inverted": "false", "frame-width": "1", "handle-offset": "0, 0", - "max-value": "1", - "min-value": "0", "mode": "free click", - "mouse-enabled": "true", "opacity": "1", "orientation": "horizontal", "origin": "40, 140", @@ -397,16 +364,12 @@ "autosize": "left right top ", "class": "CSegmentButton", "control-tag": "StringList", - "default-value": "0.5", "font": "~ NormalFont", "frame-color": "~ BlackCColor", "frame-width": "1", "gradient": "Default TextButton Gradient", "gradient-highlighted": "Default TextButton Gradient Highlighted", "icon-text-margin": "0", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "40, 170", "round-radius": "3", @@ -427,7 +390,6 @@ "attributes": { "class": "CTextButton", "control-tag": "ShowAlert", - "default-value": "0.5", "font": "~ SystemFont", "frame-color": "~ BlackCColor", "frame-color-highlighted": "~ BlackCColor", @@ -437,9 +399,6 @@ "icon-position": "left", "icon-text-margin": "0", "kick-style": "false", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "170, 210", "round-radius": "3", @@ -461,15 +420,11 @@ "background-offset": "0, 0", "class": "CParamDisplay", "control-tag": "Test", - "default-value": "0.5", "font": "~ NormalFont", "font-antialias": "true", "font-color": "~ WhiteCColor", "frame-color": "~ WhiteCColor", "frame-width": "1", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "310, 50", "round-rect-radius": "3", @@ -500,16 +455,12 @@ "background-offset": "0, 0", "class": "CTextEdit", "control-tag": "Test", - "default-value": "0.5", "font": "~ NormalFont", "font-antialias": "true", "font-color": "~ WhiteCColor", "frame-color": "~ WhiteCColor", "frame-width": "1", "immediate-text-change": "false", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "310, 80", "round-rect-radius": "3", @@ -528,7 +479,6 @@ "text-inset": "0, 0", "text-rotation": "0", "text-shadow-offset": "1, 1", - "title": "0.00", "tooltip": "Text Edit", "transparent": "false", "value-precision": "2", @@ -543,16 +493,12 @@ "background-offset": "0, 0", "class": "CTextEdit", "control-tag": "StepTest", - "default-value": "0.5", "font": "~ NormalFont", "font-antialias": "true", "font-color": "~ WhiteCColor", "frame-color": "~ WhiteCColor", "frame-width": "1", "immediate-text-change": "false", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "310, 110", "round-rect-radius": "6", @@ -571,7 +517,6 @@ "text-inset": "0, 0", "text-rotation": "0", "text-shadow-offset": "1, 1", - "title": "0", "tooltip": "Text Edit", "transparent": "false", "value-precision": "2", @@ -586,17 +531,13 @@ "background-offset": "0, 0", "class": "COptionMenu", "control-tag": "StepTest", - "default-value": "0.5", "font": "~ NormalFont", "font-antialias": "true", "font-color": "~ WhiteCColor", "frame-color": "~ WhiteCColor", "frame-width": "1", - "max-value": "4", "menu-check-style": "true", "menu-popup-style": "true", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "310, 140", "round-rect-radius": "6", @@ -627,17 +568,13 @@ "background-offset": "0, 0", "class": "COptionMenu", "control-tag": "StringList", - "default-value": "0.5", "font": "~ NormalFont", "font-antialias": "true", "font-color": "~ WhiteCColor", "frame-color": "~ WhiteCColor", "frame-width": "1", - "max-value": "4", "menu-check-style": "true", "menu-popup-style": "true", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "310, 170", "round-rect-radius": "6", @@ -668,16 +605,12 @@ "background-offset": "0, 0", "class": "CTextEdit", "control-tag": "StringList", - "default-value": "0.5", "font": "~ NormalFont", "font-antialias": "true", "font-color": "~ WhiteCColor", "frame-color": "~ WhiteCColor", "frame-width": "1", "immediate-text-change": "false", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "310, 200", "round-rect-radius": "6", @@ -696,7 +629,6 @@ "text-inset": "0, 0", "text-rotation": "0", "text-shadow-offset": "1, 1", - "title": "one", "tooltip": "Text Edit", "transparent": "false", "value-precision": "2", @@ -781,7 +713,6 @@ "attributes": { "class": "CTextButton", "control-tag": "ShowAlert2", - "default-value": "0.5", "font": "~ SystemFont", "frame-color": "~ BlackCColor", "frame-color-highlighted": "~ BlackCColor", @@ -791,9 +722,6 @@ "icon-position": "left", "icon-text-margin": "0", "kick-style": "false", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "170, 235", "round-radius": "3", @@ -812,7 +740,6 @@ "attributes": { "class": "CTextButton", "control-tag": "ShowAlert3", - "default-value": "0.5", "font": "~ SystemFont", "frame-color": "~ BlackCColor", "frame-color-highlighted": "~ BlackCColor", @@ -822,9 +749,6 @@ "icon-position": "left", "icon-text-margin": "0", "kick-style": "false", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "170, 260", "round-radius": "3", @@ -847,14 +771,12 @@ "bitmap": "knob", "class": "CAnimKnob", "control-tag": "Test", - "default-value": "0.5", + "height-of-one-image": "50", "inverse-bitmap": "false", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "340, 235", "size": "50, 50", + "sub-pixmaps": "6", "tooltip": "Knob", "transparent": "false", "value-inset": "5", @@ -881,13 +803,9 @@ "corona-line-cap-butt": "true", "corona-outline": "true", "corona-outline-width-add": "2", - "default-value": "0.5", "handle-color": "~ WhiteCColor", "handle-line-width": "8", "handle-shadow-color": "~ BlackCColor", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "270, 225", "size": "70, 70", @@ -907,15 +825,11 @@ "background-offset": "0, 0", "class": "CParamDisplay", "control-tag": "MutableString", - "default-value": "0.5", "font": "~ NormalFont", "font-antialias": "true", "font-color": "~ BlackCColor", "frame-color": "~ BlackCColor", "frame-width": "-1", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "120, 10", "round-rect-radius": "3", @@ -984,10 +898,6 @@ "class": "CControl", "control-tag": "Activate", "custom-view-name": "Native Checkbox", - "default-value": "0.5", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "5, 40", "size": "110, 20", @@ -1000,11 +910,8 @@ "CControl": { "attributes": { "class": "CControl", + "control-tag": "Activate", "custom-view-name": "Native Push Button", - "default-value": "0.5", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "0, 65", "size": "100, 24", @@ -1017,11 +924,8 @@ "CControl": { "attributes": { "class": "CControl", + "control-tag": "Activate", "custom-view-name": "Native OnOff Button", - "default-value": "0.5", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "0, 90", "size": "100, 25", @@ -1034,11 +938,8 @@ "CControl": { "attributes": { "class": "CControl", + "control-tag": "Activate", "custom-view-name": "Native Radio Button", - "default-value": "0.5", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "5, 120", "size": "100, 20", diff --git a/vstgui/standalone/examples/standalone/resource/testpopup.uidesc b/vstgui/standalone/examples/standalone/resource/testpopup.uidesc index fe645823a..e58a9fa10 100644 --- a/vstgui/standalone/examples/standalone/resource/testpopup.uidesc +++ b/vstgui/standalone/examples/standalone/resource/testpopup.uidesc @@ -75,7 +75,8 @@ "opacity": "1", "origin": "0, 0", "size": "340, 40", - "transparent": "true" + "transparent": "true", + "wants-focus": "false" }, "children": { "CGradientView": { @@ -88,22 +89,21 @@ "gradient-angle": "0", "gradient-style": "linear", "mouse-enabled": "false", - "opacity": "1", + "opacity": "0.9", "origin": "0, 0", "radial-center": "0.5, 0.5", "radial-radius": "0.8", - "round-rect-radius": "0", + "round-rect-radius": "2", "size": "340, 40", - "transparent": "false" + "transparent": "false", + "wants-focus": "false" } }, "CSlider": { "attributes": { - "background-offset": "0, 0", "bitmap-offset": "0, 0", "class": "CSlider", "control-tag": "Test", - "default-value": "0.5", "draw-back": "true", "draw-back-color": "~ WhiteCColor", "draw-frame": "true", @@ -112,18 +112,16 @@ "draw-value-color": "~ RedCColor", "draw-value-from-center": "false", "draw-value-inverted": "false", + "frame-width": "1", "handle-offset": "0, 0", - "max-value": "1", - "min-value": "0", "mode": "free click", - "mouse-enabled": "true", "opacity": "1", "orientation": "horizontal", "origin": "10, 10", "reverse-orientation": "false", "size": "220, 20", "transparent": "false", - "transparent-handle": "true", + "wants-focus": "true", "wheel-inc-value": "0.1", "zoom-factor": "10" } @@ -134,19 +132,16 @@ "background-offset": "0, 0", "class": "CTextEdit", "control-tag": "Test", - "default-value": "0.5", "font": "~ NormalFont", "font-antialias": "true", "font-color": "~ WhiteCColor", "frame-color": "~ BlackCColor", "frame-width": "1", "immediate-text-change": "false", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", "opacity": "1", "origin": "240, 10", "round-rect-radius": "6", + "secure-style": "false", "shadow-color": "~ RedCColor", "size": "90, 20", "style-3D-in": "false", @@ -161,9 +156,9 @@ "text-inset": "0, 0", "text-rotation": "0", "text-shadow-offset": "1, 1", - "title": "0.287546", "transparent": "false", "value-precision": "2", + "wants-focus": "true", "wheel-inc-value": "0.1" } } diff --git a/vstgui/standalone/examples/standalone/source/testappdelegate.cpp b/vstgui/standalone/examples/standalone/source/testappdelegate.cpp index 82db77376..93e61d2ba 100644 --- a/vstgui/standalone/examples/standalone/source/testappdelegate.cpp +++ b/vstgui/standalone/examples/standalone/source/testappdelegate.cpp @@ -222,6 +222,7 @@ class DatePickerController : public DelegationController Delegate::Delegate () : Application::DelegateAdapter ({"VSTGUI Standalone", "1.0.0", VSTGUI_STANDALONE_APP_URI}) { + CFrame::kDefaultKnobMode = CKnobMode::kLinearMode; } //------------------------------------------------------------------------ diff --git a/vstgui/standalone/source/platform/mac/macapplication.mm b/vstgui/standalone/source/platform/mac/macapplication.mm index f8dabcbbf..eabbed43f 100644 --- a/vstgui/standalone/source/platform/mac/macapplication.mm +++ b/vstgui/standalone/source/platform/mac/macapplication.mm @@ -709,6 +709,24 @@ - (void)application:(nonnull NSApplication*)sender openFiles:(nonnull NSArray (instance), path, MAX_PATH))) { @@ -325,6 +320,11 @@ int APIENTRY wWinMain (_In_ HINSTANCE instance, _In_opt_ HINSTANCE prevInstance, if (FAILED (hr)) return FALSE; + auto& hidpiSupport = VSTGUI::HiDPISupport::instance (); + if (!hidpiSupport.setProcessDpiAwarnessContext ( + VSTGUI::HiDPISupport::AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + hidpiSupport.setProcessDpiAwareness (VSTGUI::HiDPISupport::PROCESS_PER_MONITOR_DPI_AWARE); + VSTGUI::init (instance); VSTGUI::getPlatformFactory ().asWin32Factory ()->useD2DHardwareRenderer (true); VSTGUI::Standalone::Platform::Win32::Application app; diff --git a/vstgui/standalone/source/platform/win32/win32window.cpp b/vstgui/standalone/source/platform/win32/win32window.cpp index 012fa98cb..adc348074 100644 --- a/vstgui/standalone/source/platform/win32/win32window.cpp +++ b/vstgui/standalone/source/platform/win32/win32window.cpp @@ -24,6 +24,21 @@ #pragma comment(lib, "Dwmapi.lib") #pragma comment(lib, "Comctl32.lib") +#if NTDDI_VERSION < 0x0A00000C + +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#define DWMWA_SYSTEMBACKDROP_TYPE 38 +enum DWM_SYSTEMBACKDROP_TYPE +{ + DWMSBT_AUTO, // [Default] Let DWM automatically decide the system-drawn backdrop for this window. + DWMSBT_NONE, // Do not draw any system backdrop. + DWMSBT_MAINWINDOW, // Draw the backdrop material effect corresponding to a long-lived window. + DWMSBT_TRANSIENTWINDOW, // Draw the backdrop material effect corresponding to a transient window. + DWMSBT_TABBEDWINDOW, // Draw the backdrop material effect corresponding to a window with a tabbed title bar. +}; + +#endif + //------------------------------------------------------------------------ namespace VSTGUI { namespace Standalone { @@ -117,6 +132,7 @@ class Window : public IWin32Window, public std::enable_shared_from_this bool hasMenu {false}; WindowStyle style; bool isPopup {false}; + bool inWmDpiScaledSize {false}; std::function frameWindowProc; }; @@ -228,10 +244,15 @@ bool Window::init (const WindowConfiguration& config, IWindowDelegate& inDelegat delegate = &inDelegate; SetWindowLongPtr (hwnd, GWLP_USERDATA, (__int3264) (LONG_PTR)this); if (style.hasBorder ()) + { hasMenu = true; + BOOL value = TRUE; + DwmSetWindowAttribute (hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof (value)); + } if (style.isTransparent ()) makeTransparent (); dwStyle = GetWindowStyle (hwnd); + return true; } @@ -459,6 +480,8 @@ void Window::makeTransparent () MARGINS margin = {-1}; auto res = DwmExtendFrameIntoClientArea (hwnd, &margin); vstgui_assert (res == S_OK); + DWM_SYSTEMBACKDROP_TYPE type = DWMSBT_NONE; + res = DwmSetWindowAttribute (hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &type, sizeof (type)); } //------------------------------------------------------------------------ @@ -643,10 +666,34 @@ LRESULT CALLBACK Window::proc (UINT message, WPARAM wParam, LPARAM lParam) return res; break; } + case WM_GETDPISCALEDSIZE: + { + auto* proposedSize = reinterpret_cast (lParam); + auto newScaleFactor = + static_cast (wParam) / static_cast (USER_DEFAULT_SCREEN_DPI); + auto clientSize = getSize (); + WINDOWINFO windowInfo {0}; + GetWindowInfo (hwnd, &windowInfo); + RECT clientRect {}; + clientRect.right = static_cast (clientSize.x * newScaleFactor); + clientRect.bottom = static_cast (clientSize.y * newScaleFactor); + HiDPISupport::instance ().adjustWindowRectExForDpi (&clientRect, windowInfo.dwStyle, + hasMenu, windowInfo.dwExStyle, + static_cast (wParam)); + proposedSize->cx = clientRect.right - clientRect.left; + proposedSize->cy = clientRect.bottom - clientRect.top; + return TRUE; + } case WM_DPICHANGED: { + inWmDpiScaledSize = true; setNewDPI (static_cast (LOWORD (wParam))); - break; + inWmDpiScaledSize = false; + + auto* rect = reinterpret_cast (lParam); + SetWindowPos (hwnd, nullptr, rect->left, rect->top, rect->right - rect->left, + rect->bottom - rect->top, SWP_NOZORDER | SWP_NOACTIVATE); + return 0; } case WM_PARENTNOTIFY: { @@ -841,15 +888,22 @@ void Window::setSize (const CPoint& newSize) initialSize = newSize; return; } + if (inWmDpiScaledSize) + { + return; + } + RECT clientRect {}; clientRect.right = static_cast (newSize.x * dpiScale); clientRect.bottom = static_cast (newSize.y * dpiScale); - AdjustWindowRectEx (&clientRect, dwStyle, hasMenu, exStyle); + HiDPISupport::instance ().adjustWindowRectExForDpi ( + &clientRect, dwStyle, hasMenu, exStyle, + static_cast (dpiScale * USER_DEFAULT_SCREEN_DPI)); LONG width = clientRect.right - clientRect.left; LONG height = clientRect.bottom - clientRect.top; - SetWindowPos (hwnd, HWND_TOP, 0, 0, width, height, - SWP_NOMOVE | SWP_NOCOPYBITS | SWP_NOACTIVATE); + SetWindowPos (hwnd, nullptr, 0, 0, width, height, + SWP_NOMOVE | SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER); if (style.isTransparent ()) { @@ -864,12 +918,14 @@ void Window::setPosition (const CPoint& newPosition) RECT clientRect {}; clientRect.right = 100; clientRect.bottom = 100; - AdjustWindowRectEx (&clientRect, dwStyle, hasMenu, exStyle); + HiDPISupport::instance ().adjustWindowRectExForDpi ( + &clientRect, dwStyle, hasMenu, exStyle, + static_cast (dpiScale * USER_DEFAULT_SCREEN_DPI)); clientRect.left += static_cast (newPosition.x); clientRect.top += static_cast (newPosition.y); - SetWindowPos (hwnd, HWND_TOP, clientRect.left, clientRect.top, 0, 0, - SWP_NOSIZE | SWP_NOACTIVATE); + SetWindowPos (hwnd, nullptr, clientRect.left, clientRect.top, 0, 0, + SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); } //------------------------------------------------------------------------ diff --git a/vstgui/standalone/source/uidescriptionwindowcontroller.cpp b/vstgui/standalone/source/uidescriptionwindowcontroller.cpp index def26b93e..e22354ef1 100644 --- a/vstgui/standalone/source/uidescriptionwindowcontroller.cpp +++ b/vstgui/standalone/source/uidescriptionwindowcontroller.cpp @@ -792,7 +792,30 @@ struct WindowController::EditImpl : WindowController::Impl Detail::saveSharedUIDescription (); int32_t flags = UIDescription::kWriteImagesIntoUIDescFile | CompressedUIDescription::kForceWriteCompressedDesc; - if (!uiDesc->save (uiDesc->getFilePath (), flags)) + + // filter out attributes we will always override with the values from the parameters + auto filter = [] (CView* view, const std::string& name) -> bool { + if (auto control = dynamic_cast (view)) + { + if (control->getTag () >= 0) + { + if (name == UIViewCreator::kAttrMinValue) + return false; + if (name == UIViewCreator::kAttrMaxValue) + return false; + if (name == UIViewCreator::kAttrDefaultValue) + return false; + if (name == UIViewCreator::kAttrMouseEnabled) + return false; + if (name == UIViewCreator::kAttrTitle && + dynamic_cast (control)) + return false; + } + } + return true; + }; + + if (!uiDesc->save (uiDesc->getFilePath (), flags, filter)) { AlertBoxConfig config; config.headline = "Saving the uidesc file failed."; diff --git a/vstgui/tests/unittest/lib/crowcolumnview_test.cpp b/vstgui/tests/unittest/lib/crowcolumnview_test.cpp new file mode 100644 index 000000000..ca842e4bb --- /dev/null +++ b/vstgui/tests/unittest/lib/crowcolumnview_test.cpp @@ -0,0 +1,315 @@ +// 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 "../../../lib/crowcolumnview.h" +#include "../unittests.h" +#include +#include + +namespace VSTGUI { + +using Rects = std::vector; + +static const CRect templateSize (0., 0., 100., 100.); +static const CRect layoutSize (0., 0., 80., 80.); + +// clang-format off +static const Rects childrenDefaultSizes = { + {0., 0., 10., 10.}, + {0., 0., 20., 20.}, + {0., 0., 30., 30.} +}; + +using ExpectedResults = std::map; + +static const ExpectedResults kRowLayoutChildrenResultSizes = { + { + CRowColumnView::kTopLeft, { + {0., 0., 10., 10.}, + {0., 10., 20., 30.}, + {0., 30., 30., 60.} + } + }, + { + CRowColumnView::kTopCenter, { + {35., 0., 45., 10.}, + {30., 10., 50., 30.}, + {25., 30., 55., 60.} + } + }, + { + CRowColumnView::kTopRight, { + {70., 0., 80., 10.}, + {60., 10., 80., 30.}, + {50., 30., 80., 60.} + } + }, + { + CRowColumnView::kMiddleLeft, { + {0., 10., 10., 20.}, + {0., 20., 20., 40.}, + {0., 40., 30., 70.} + } + }, + { + CRowColumnView::kMiddleCenter, { + {35., 10., 45., 20.}, + {30., 20., 50., 40.}, + {25., 40., 55., 70.} + } + }, + { + CRowColumnView::kMiddleRight, { + {70., 10., 80., 20.}, + {60., 20., 80., 40.}, + {50., 40., 80., 70.} + } + }, + { + CRowColumnView::kBottomLeft, { + {0., 20., 10., 30.}, + {0., 30., 20., 50.}, + {0., 50., 30., 80.} + } + }, + { + CRowColumnView::kBottomCenter, { + {35., 20., 45., 30.}, + {30., 30., 50., 50.}, + {25., 50., 55., 80.} + } + }, + { + CRowColumnView::kBottomRight, { + {70., 20., 80., 30.}, + {60., 30., 80., 50.}, + {50., 50., 80., 80.} + } + } +}; + +static const ExpectedResults kColumnLayoutChildrenResultSizes = { + { + CRowColumnView::kTopLeft, { + {0., 0., 10., 10.}, + {10., 0., 30., 20.}, + {30., 0., 60., 30.} + } + }, + { + CRowColumnView::kTopCenter, { + {10., 0., 20., 10.}, + {20., 0., 40., 20.}, + {40., 0., 70., 30.} + } + }, + { + CRowColumnView::kTopRight, { + {20., 0., 30., 10.}, + {30., 0., 50., 20.}, + {50., 0., 80., 30.} + } + }, + { + CRowColumnView::kMiddleLeft, { + {0., 35., 10., 45.}, + {10., 30., 30., 50.}, + {30., 25., 60., 55.} + } + }, + { + CRowColumnView::kMiddleCenter, { + {10., 35., 20., 45.}, + {20., 30., 40., 50.}, + {40., 25., 70., 55.} + } + }, + { + CRowColumnView::kMiddleRight, { + {20., 35., 30., 45.}, + {30., 30., 50., 50.}, + {50., 25., 80., 55.} + } + }, + { + CRowColumnView::kBottomLeft, { + {0., 70., 10., 80.}, + {10., 60., 30., 80.}, + {30., 50., 60., 80.} + } + }, + { + CRowColumnView::kBottomCenter, { + {10., 70., 20., 80.}, + {20., 60., 40., 80.}, + {40., 50., 70., 80.} + } + }, + { + CRowColumnView::kBottomRight, { + {20., 70., 30., 80.}, + {30., 60., 50., 80.}, + {50., 50., 80., 80.} + } + } +}; + +static const ExpectedResults kRowLayoutChildrenResultSizesWithSpacing = { + { + CRowColumnView::kMiddleCenter, { + {35., 6., 45., 16.}, + {30., 20., 50., 40.}, + {25., 44., 55., 74.} + } + } +}; +// clang-format on + +struct TestData +{ + CRowColumnView::LayoutStyle layoutStyle = CRowColumnView::LayoutStyle::kTopLeft; + CRowColumnView::Style style = CRowColumnView::Style::kRowStyle; + double spacing = 0.; + ExpectedResults expected; +}; + +auto testWithLayoutStyle (const TestData& testData) -> void +{ + const auto& expected = testData.expected.find (testData.layoutStyle)->second; + auto rowColumnView = owned (new CRowColumnView (layoutSize)); + rowColumnView->setStyle (testData.style); + rowColumnView->setLayoutStyle (testData.layoutStyle); + rowColumnView->setSpacing (testData.spacing); + + for (auto& rect : childrenDefaultSizes) + { + auto child = new CView (rect); + rowColumnView->CViewContainer::addView (child); + rowColumnView->layoutViews (); + } + + size_t i = 0; + rowColumnView->forEachChild ([&] (CView* child) { + const auto& childrenResults = testData.expected.find (testData.layoutStyle); + auto viewSize = child->getViewSize (); + EXPECT (viewSize == expected.at (i)) + i++; + }); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutTopLeftStyle) +{ + testWithLayoutStyle ( + {CRowColumnView::kTopLeft, CRowColumnView::kRowStyle, 0., kRowLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutTopCenterStyle) +{ + testWithLayoutStyle ( + {CRowColumnView::kTopCenter, CRowColumnView::kRowStyle, 0., kRowLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutTopRightStyle) +{ + testWithLayoutStyle ( + {CRowColumnView::kTopRight, CRowColumnView::kRowStyle, 0., kRowLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutMiddleLeftStyle) +{ + testWithLayoutStyle ({CRowColumnView::kMiddleLeft, CRowColumnView::kRowStyle, 0., + kRowLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutMiddleCenterStyle) +{ + testWithLayoutStyle ({CRowColumnView::kMiddleCenter, CRowColumnView::kRowStyle, 0., + kRowLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutMiddleRightStyle) +{ + testWithLayoutStyle ({CRowColumnView::kMiddleRight, CRowColumnView::kRowStyle, 0., + kRowLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutBottomLeftStyle) +{ + testWithLayoutStyle ({CRowColumnView::kBottomLeft, CRowColumnView::kRowStyle, 0., + kRowLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutBottomCenterStyle) +{ + testWithLayoutStyle ({CRowColumnView::kBottomCenter, CRowColumnView::kRowStyle, 0., + kRowLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutBottomRightStyle) +{ + testWithLayoutStyle ({CRowColumnView::kBottomRight, CRowColumnView::kRowStyle, 0., + kRowLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, ColumnLayoutTopLeftStyle) +{ + testWithLayoutStyle ({CRowColumnView::kTopLeft, CRowColumnView::kColumnStyle, 0., + kColumnLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, ColumnLayoutTopCenterStyle) +{ + testWithLayoutStyle ({CRowColumnView::kTopCenter, CRowColumnView::kColumnStyle, 0., + kColumnLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, ColumnLayoutTopRightStyle) +{ + testWithLayoutStyle ({CRowColumnView::kTopRight, CRowColumnView::kColumnStyle, 0., + kColumnLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, ColumnLayoutMiddleLeftStyle) +{ + testWithLayoutStyle ({CRowColumnView::kMiddleLeft, CRowColumnView::kColumnStyle, 0., + kColumnLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, ColumnLayoutMiddleCenterStyle) +{ + testWithLayoutStyle ({CRowColumnView::kMiddleCenter, CRowColumnView::kColumnStyle, 0., + kColumnLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, ColumnLayoutMiddleRightStyle) +{ + testWithLayoutStyle ({CRowColumnView::kMiddleRight, CRowColumnView::kColumnStyle, 0., + kColumnLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, ColumnLayoutBottomLeftStyle) +{ + testWithLayoutStyle ({CRowColumnView::kBottomLeft, CRowColumnView::kColumnStyle, 0., + kColumnLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, ColumnLayoutBottomCenterStyle) +{ + testWithLayoutStyle ({CRowColumnView::kBottomCenter, CRowColumnView::kColumnStyle, 0., + kColumnLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, ColumnLayoutBottomRightStyle) +{ + testWithLayoutStyle ({CRowColumnView::kBottomRight, CRowColumnView::kColumnStyle, 0., + kColumnLayoutChildrenResultSizes}); +} + +TEST_CASE (CRowColumnViewTest, RowLayoutMiddleCenterStyleWithSpacing) +{ + testWithLayoutStyle ({CRowColumnView::kMiddleCenter, CRowColumnView::kRowStyle, 4., + kRowLayoutChildrenResultSizesWithSpacing}); +} + +} // VSTGUI diff --git a/vstgui/tests/unittest/uidescription/uiviewcreator/cknobcreator_test.cpp b/vstgui/tests/unittest/uidescription/uiviewcreator/cknobcreator_test.cpp index 3f761eae5..0d47e6224 100644 --- a/vstgui/tests/unittest/uidescription/uiviewcreator/cknobcreator_test.cpp +++ b/vstgui/tests/unittest/uidescription/uiviewcreator/cknobcreator_test.cpp @@ -31,6 +31,13 @@ TEST_CASE (CKnobCreatorTest, AngleRange) }); } +TEST_CASE (CKnobCreatorTest, KnobRange) +{ + DummyUIDescription uidesc; + testAttribute (kCKnob, kAttrKnobRange, 200., &uidesc, + [&] (CKnob* v) { return v->getKnobRange () == 200.; }); +} + TEST_CASE (CKnobCreatorTest, ValueInset) { DummyUIDescription uidesc; diff --git a/vstgui/tests/unittest/uidescription/uiviewcreator/crowcolumnviewcreator_test.cpp b/vstgui/tests/unittest/uidescription/uiviewcreator/crowcolumnviewcreator_test.cpp index 60a3f1fe4..a83b8bff9 100644 --- a/vstgui/tests/unittest/uidescription/uiviewcreator/crowcolumnviewcreator_test.cpp +++ b/vstgui/tests/unittest/uidescription/uiviewcreator/crowcolumnviewcreator_test.cpp @@ -16,82 +16,84 @@ using namespace UIViewCreator; TEST_CASE (CRowColumnViewCreatorTest, RowStyle) { testAttribute ( - kCRowColumnView, kAttrRowStyle, true, nullptr, - [] (CRowColumnView* v) { return v->getStyle () == CRowColumnView::kRowStyle; }); + kCRowColumnView, kAttrRowStyle, true, nullptr, + [] (CRowColumnView* v) { return v->getStyle () == CRowColumnView::kRowStyle; }); } TEST_CASE (CRowColumnViewCreatorTest, ColumnStyle) { testAttribute ( - kCRowColumnView, kAttrRowStyle, false, nullptr, - [] (CRowColumnView* v) { return v->getStyle () == CRowColumnView::kColumnStyle; }); + kCRowColumnView, kAttrRowStyle, false, nullptr, + [] (CRowColumnView* v) { return v->getStyle () == CRowColumnView::kColumnStyle; }); } TEST_CASE (CRowColumnViewCreatorTest, Spacing) { testAttribute (kCRowColumnView, kAttrSpacing, 5., nullptr, - [] (CRowColumnView* v) { return v->getSpacing () == 5.; }); + [] (CRowColumnView* v) { return v->getSpacing () == 5.; }); } TEST_CASE (CRowColumnViewCreatorTest, Margin) { CRect margin (5, 6, 7, 8); testAttribute (kCRowColumnView, kAttrMargin, margin, nullptr, - [&] (CRowColumnView* v) { return v->getMargin () == margin; }); + [&] (CRowColumnView* v) { return v->getMargin () == margin; }); } TEST_CASE (CRowColumnViewCreatorTest, AnimateViewResizing) { testAttribute (kCRowColumnView, kAttrAnimateViewResizing, true, nullptr, - [] (CRowColumnView* v) { return v->isAnimateViewResizing (); }); + [] (CRowColumnView* v) { return v->isAnimateViewResizing (); }); } TEST_CASE (CRowColumnViewCreatorTest, EqualSizeLayoutStretch) { testAttribute ( - kCRowColumnView, kAttrEqualSizeLayout, "stretch", nullptr, - [] (CRowColumnView* v) { return v->getLayoutStyle () == CRowColumnView::kStretchEqualy; }); + kCRowColumnView, kAttrEqualSizeLayout, "stretch", nullptr, + [] (CRowColumnView* v) { return v->getLayoutStyle () == CRowColumnView::kStretchEqualy; }); } TEST_CASE (CRowColumnViewCreatorTest, EqualSizeLayoutCenter) { testAttribute ( - kCRowColumnView, kAttrEqualSizeLayout, "center", nullptr, - [] (CRowColumnView* v) { return v->getLayoutStyle () == CRowColumnView::kCenterEqualy; }); + kCRowColumnView, kAttrEqualSizeLayout, "center", nullptr, + [] (CRowColumnView* v) { return v->getLayoutStyle () == CRowColumnView::kCenterEqualy; }); } TEST_CASE (CRowColumnViewCreatorTest, EqualSizeLayoutRightBottom) { testAttribute ( - kCRowColumnView, kAttrEqualSizeLayout, "right-bottom", nullptr, [] (CRowColumnView* v) { - return v->getLayoutStyle () == CRowColumnView::kRightBottomEqualy; - }); + kCRowColumnView, kAttrEqualSizeLayout, "right-bottom", nullptr, [] (CRowColumnView* v) { + return v->getLayoutStyle () == CRowColumnView::kRightBottomEqualy; + }); } TEST_CASE (CRowColumnViewCreatorTest, EqualSizeLayoutLeftTop) { testAttribute ( - kCRowColumnView, kAttrEqualSizeLayout, "left-top", nullptr, - [] (CRowColumnView* v) { return v->getLayoutStyle () == CRowColumnView::kLeftTopEqualy; }); + kCRowColumnView, kAttrEqualSizeLayout, "left-top", nullptr, + [] (CRowColumnView* v) { return v->getLayoutStyle () == CRowColumnView::kLeftTopEqualy; }); } TEST_CASE (CRowColumnViewCreatorTest, AnimationTime) { testAttribute ( - kCRowColumnView, kAttrViewResizeAnimationTime, 100, nullptr, - [] (CRowColumnView* v) { return v->getViewResizeAnimationTime () == 100; }); + kCRowColumnView, kAttrViewResizeAnimationTime, 100, nullptr, + [] (CRowColumnView* v) { return v->getViewResizeAnimationTime () == 100; }); } TEST_CASE (CRowColumnViewCreatorTest, EqualSizeLayoutValues) { testPossibleValues (kCRowColumnView, kAttrEqualSizeLayout, nullptr, - {"left-top", "stretch", "center", "right-bottom"}); + {"left-top", "stretch", "center", "right-bottom", "top-left", "top-center", + "top-right", "middle-left", "middle-center", "middle-right", "bottom-left", + "bottom-center", "bottom-right"}); } TEST_CASE (CRowColumnViewCreatorTest, HideClippedSubviews) { testAttribute (kCRowColumnView, kAttrHideClippedSubviews, true, nullptr, - [] (CRowColumnView* v) { return v->hideClippedSubviews (); }); + [] (CRowColumnView* v) { return v->hideClippedSubviews (); }); } } // VSTGUI diff --git a/vstgui/uidescription/CMakeLists.txt b/vstgui/uidescription/CMakeLists.txt index 98414babc..d402290dd 100644 --- a/vstgui/uidescription/CMakeLists.txt +++ b/vstgui/uidescription/CMakeLists.txt @@ -178,7 +178,6 @@ endif() add_library(${target} STATIC ${${target}_sources}) add_dependencies(${target} vstgui) -target_link_libraries(${target} PRIVATE vstgui) target_compile_definitions(${target} ${VSTGUI_COMPILE_DEFINITIONS}) vstgui_set_cxx_version(${target} 17) vstgui_source_group_by_folder(${target}) @@ -206,6 +205,7 @@ if(LINUX) PRIVATE ${VSTGUI_GENERATED_DIR} ) + target_link_libraries(${target} PRIVATE vstgui) endif() ########################################################################################## find_package(EXPAT) diff --git a/vstgui/uidescription/detail/uiviewcreatorattributes.h b/vstgui/uidescription/detail/uiviewcreatorattributes.h index 2516394dc..01efff7d6 100644 --- a/vstgui/uidescription/detail/uiviewcreatorattributes.h +++ b/vstgui/uidescription/detail/uiviewcreatorattributes.h @@ -216,6 +216,7 @@ static const std::string kAttrSegmentNames = "segment-names"; //----------------------------------------------------------------------------- static const std::string kAttrAngleStart = "angle-start"; static const std::string kAttrAngleRange = "angle-range"; +static const std::string kAttrKnobRange = "knob-range"; static const std::string kAttrValueInset = "value-inset"; static const std::string kAttrCoronaInset = "corona-inset"; static const std::string kAttrCoronaColor = "corona-color"; diff --git a/vstgui/uidescription/editing/uieditmenucontroller.cpp b/vstgui/uidescription/editing/uieditmenucontroller.cpp index d8dbadc18..59bb6f569 100644 --- a/vstgui/uidescription/editing/uieditmenucontroller.cpp +++ b/vstgui/uidescription/editing/uieditmenucontroller.cpp @@ -30,6 +30,15 @@ UIEditMenuController::UIEditMenuController (IController* baseController, UISelec { } +//------------------------------------------------------------------------ +UIEditMenuController::~UIEditMenuController () noexcept +{ + if (editMenu) + editMenu->unregisterViewListener (this); + if (fileMenu) + fileMenu->unregisterViewListener (this); +} + //---------------------------------------------------------------------------------------------------- static void addEntriesToMenu (const UIEditing::MenuEntry* entries, COptionMenu* menu, ICommandMenuItemTarget* menuItemTarget, int32_t& index) { @@ -615,6 +624,21 @@ static void copyMenuItems (COptionMenu* src, COptionMenu* dst) } } +//------------------------------------------------------------------------ +void UIEditMenuController::viewRemoved (CView* view) +{ + if (view == editMenu) + { + view->unregisterViewListener (this); + editMenu = nullptr; + } + else if (view == fileMenu) + { + view->unregisterViewListener (this); + fileMenu = nullptr; + } +} + //---------------------------------------------------------------------------------------------------- CView* UIEditMenuController::verifyView (CView* view, const UIAttributes& attributes, const IUIDescription*) { @@ -628,7 +652,10 @@ CView* UIEditMenuController::verifyView (CView* view, const UIAttributes& attrib if (editMenu) copyMenuItems (editMenu, menu); else + { createEditMenu (menu); + menu->registerViewListener (this); + } editMenu = menu; break; } @@ -637,7 +664,10 @@ CView* UIEditMenuController::verifyView (CView* view, const UIAttributes& attrib if (fileMenu) copyMenuItems (fileMenu, menu); else + { createFileMenu (menu); + menu->registerViewListener (this); + } fileMenu = menu; break; } diff --git a/vstgui/uidescription/editing/uieditmenucontroller.h b/vstgui/uidescription/editing/uieditmenucontroller.h index e54ac86d8..f9cc94e95 100644 --- a/vstgui/uidescription/editing/uieditmenucontroller.h +++ b/vstgui/uidescription/editing/uieditmenucontroller.h @@ -13,6 +13,7 @@ #include "../delegationcontroller.h" #include "../../lib/controls/icommandmenuitemtarget.h" #include "../../lib/events.h" +#include "../../lib/iviewlistener.h" namespace VSTGUI { class IActionPerformer; @@ -144,11 +145,14 @@ static const MenuEntry fileMenu[] = { } // UIEditing //---------------------------------------------------------------------------------------------------- -class UIEditMenuController : public CBaseObject, public DelegationController, public CommandMenuItemTargetAdapter +class UIEditMenuController : public CBaseObject, + public DelegationController, + public CommandMenuItemTargetAdapter, + public ViewListenerAdapter { public: UIEditMenuController (IController* baseController, UISelection* selection, UIUndoManager* undoManager, UIDescription* description, IActionPerformer* actionPerformer); - ~UIEditMenuController () noexcept override = default; + ~UIEditMenuController () noexcept override; COptionMenu* getFileMenu () const { return fileMenu; } COptionMenu* getEditMenu () const { return editMenu; } @@ -170,6 +174,7 @@ class UIEditMenuController : public CBaseObject, public DelegationController, pu void createEditMenu (COptionMenu* menu); void createFileMenu (COptionMenu* menu); + void viewRemoved (CView* view) override; CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override; IControlListener* getControlListener (UTF8StringPtr name) override { return this; } void controlBeginEdit (CControl* pControl) override; diff --git a/vstgui/uidescription/uidescription.cpp b/vstgui/uidescription/uidescription.cpp index 7adf10bcb..adec79c5e 100644 --- a/vstgui/uidescription/uidescription.cpp +++ b/vstgui/uidescription/uidescription.cpp @@ -567,7 +567,8 @@ Detail::UINode* UIDescription::findNodeForView (CView* view) const } //----------------------------------------------------------------------------- -bool UIDescription::storeViews (const std::list& views, OutputStream& stream, UIAttributes* customData) const +bool UIDescription::storeViews (const std::list& views, OutputStream& stream, + SharedPointer customData) const { auto nodeList = makeOwned (false); for (auto& view : views) @@ -599,7 +600,6 @@ bool UIDescription::storeViews (const std::list& views, OutputStream& st UINode* customNode = new UINode (Detail::MainNodeNames::kCustom, customData); nodeList->add (customNode); customNode->forget (); - customData->remember (); } UINode baseNode ("vstgui-ui-description-view-list", nodeList); return Detail::UIJsonDescWriter::write (stream, &baseNode, false); diff --git a/vstgui/uidescription/uidescription.h b/vstgui/uidescription/uidescription.h index afcb2f665..5ad547dc3 100644 --- a/vstgui/uidescription/uidescription.h +++ b/vstgui/uidescription/uidescription.h @@ -52,7 +52,8 @@ class UIDescription : public NonAtomicReferenceCounted, public IUIDescription AttributeSaveFilterFunc func = nullptr); virtual bool saveWindowsRCFile (UTF8StringPtr filename); - bool storeViews (const std::list& views, OutputStream& stream, UIAttributes* customData = nullptr) const; + bool storeViews (const std::list& views, OutputStream& stream, + SharedPointer customData = {}) const; bool restoreViews (InputStream& stream, std::list >& views, UIAttributes** customData = nullptr); UTF8StringPtr getFilePath () const; diff --git a/vstgui/uidescription/viewcreator/knobcreator.cpp b/vstgui/uidescription/viewcreator/knobcreator.cpp index fbe78dc6d..5ba6f4187 100644 --- a/vstgui/uidescription/viewcreator/knobcreator.cpp +++ b/vstgui/uidescription/viewcreator/knobcreator.cpp @@ -35,6 +35,8 @@ bool KnobBaseCreator::apply (CView* view, const UIAttributes& attributes, d = d / 180.f * static_cast (Constants::pi); knob->setRangeAngle (static_cast (d)); } + if (attributes.getDoubleAttribute (kAttrKnobRange, d)) + knob->setKnobRange (static_cast (d)); if (attributes.getDoubleAttribute (kAttrValueInset, d)) knob->setInsetValue (d); if (attributes.getDoubleAttribute (kAttrZoomFactor, d)) @@ -48,6 +50,7 @@ bool KnobBaseCreator::getAttributeNames (StringList& attributeNames) const { attributeNames.emplace_back (kAttrAngleStart); attributeNames.emplace_back (kAttrAngleRange); + attributeNames.emplace_back (kAttrKnobRange); attributeNames.emplace_back (kAttrValueInset); attributeNames.emplace_back (kAttrZoomFactor); return true; @@ -60,6 +63,8 @@ auto KnobBaseCreator::getAttributeType (const string& attributeName) const -> At return kFloatType; if (attributeName == kAttrAngleRange) return kFloatType; + if (attributeName == kAttrKnobRange) + return kFloatType; if (attributeName == kAttrValueInset) return kFloatType; if (attributeName == kAttrZoomFactor) @@ -87,6 +92,11 @@ bool KnobBaseCreator::getAttributeValue (CView* view, const string& attributeNam UIAttributes::doubleToString ((knob->getRangeAngle () / Constants::pi * 180.), 5); return true; } + if (attributeName == kAttrKnobRange) + { + stringValue = UIAttributes::doubleToString (knob->getKnobRange (), 5); + return true; + } if (attributeName == kAttrValueInset) { stringValue = UIAttributes::doubleToString (knob->getInsetValue ()); diff --git a/vstgui/uidescription/viewcreator/rowcolumnviewcreator.cpp b/vstgui/uidescription/viewcreator/rowcolumnviewcreator.cpp index 9fb0fde48..be188a1b8 100644 --- a/vstgui/uidescription/viewcreator/rowcolumnviewcreator.cpp +++ b/vstgui/uidescription/viewcreator/rowcolumnviewcreator.cpp @@ -18,7 +18,10 @@ namespace UIViewCreator { //------------------------------------------------------------------------ auto RowColumnViewCreator::layoutStrings () -> LayoutStrings& { - static LayoutStrings strings = {"left-top", "center", "right-bottom", "stretch"}; + static LayoutStrings strings = { + "left-top", "center", "right-bottom", "stretch", "top-left", + "top-center", "top-right", "middle-left", "middle-center", "middle-right", + "bottom-left", "bottom-center", "bottom-right"}; return strings; } @@ -81,7 +84,7 @@ bool RowColumnViewCreator::apply (CView* view, const UIAttributes& attributes, attr = attributes.getAttributeValue (kAttrEqualSizeLayout); if (attr) { - for (auto index = 0u; index <= CRowColumnView::kStretchEqualy; ++index) + for (auto index = 0u; index <= CRowColumnView::kBottomRight; ++index) { if (*attr == layoutStrings ()[index]) { diff --git a/vstgui/uidescription/viewcreator/rowcolumnviewcreator.h b/vstgui/uidescription/viewcreator/rowcolumnviewcreator.h index be9e626d7..7ca2a28ae 100644 --- a/vstgui/uidescription/viewcreator/rowcolumnviewcreator.h +++ b/vstgui/uidescription/viewcreator/rowcolumnviewcreator.h @@ -19,18 +19,18 @@ struct RowColumnViewCreator : ViewCreatorAdapter IdStringPtr getBaseViewName () const override; UTF8StringPtr getDisplayName () const override; CView* create (const UIAttributes& attributes, - const IUIDescription* description) const override; + const IUIDescription* description) const override; bool apply (CView* view, const UIAttributes& attributes, - const IUIDescription* description) const override; + const IUIDescription* description) const override; bool getAttributeNames (StringList& attributeNames) const override; AttrType getAttributeType (const string& attributeName) const override; bool getAttributeValue (CView* view, const string& attributeName, string& stringValue, - const IUIDescription* desc) const override; + const IUIDescription* desc) const override; bool getPossibleListValues (const string& attributeName, - ConstStringPtrList& values) const override; + ConstStringPtrList& values) const override; private: - using LayoutStrings = std::array; + using LayoutStrings = std::array; static LayoutStrings& layoutStrings (); };