Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
+438
View File
@@ -0,0 +1,438 @@
##########################################################################################
# VSTGUI Library
##########################################################################################
set(target vstgui)
option(
VSTGUI_TEXTRENDERING_LEGACY_INCONSISTENCY
"Use the legacy platform inconsistency text rendering"
OFF
)
if(${VSTGUI_TEXTRENDERING_LEGACY_INCONSISTENCY})
target_compile_definitions(${target} PRIVATE VSTGUI_TEXTRENDERING_LEGACY_INCONSISTENCY=1)
endif()
set(${target}_common_sources
animation/animations.cpp
animation/animations.h
animation/animator.cpp
animation/animator.h
animation/ianimationtarget.h
animation/itimingfunction.h
animation/timingfunctions.cpp
animation/timingfunctions.h
algorithm.h
cbitmap.cpp
cbitmap.h
cbitmapfilter.cpp
cbitmapfilter.h
cbuttonstate.h
cclipboard.cpp
cclipboard.h
ccolor.cpp
ccolor.h
cdatabrowser.cpp
cdatabrowser.h
cdrawcontext.cpp
cdrawcontext.h
cdrawdefs.h
cdrawmethods.cpp
cdrawmethods.h
cdropsource.cpp
cdropsource.h
cexternalview.cpp
cexternalview.h
cfileselector.cpp
cfileselector.h
cfont.cpp
cfont.h
cframe.cpp
cframe.h
cgradient.cpp
cgradient.h
cgradientview.cpp
cgradientview.h
cgraphicspath.cpp
cgraphicspath.h
cgraphicstransform.h
cinvalidrectlist.h
clayeredviewcontainer.cpp
clayeredviewcontainer.h
clinestyle.cpp
clinestyle.h
ctexteditor.h
ctexteditor.cpp
coffscreencontext.cpp
coffscreencontext.h
controls/cautoanimation.cpp
controls/cautoanimation.h
controls/cbuttons.cpp
controls/cbuttons.h
controls/ccolorchooser.cpp
controls/ccolorchooser.h
controls/ccontrol.cpp
controls/ccontrol.h
controls/cfontchooser.cpp
controls/cfontchooser.h
controls/cknob.cpp
controls/cknob.h
controls/clistcontrol.cpp
controls/clistcontrol.h
controls/cmoviebitmap.cpp
controls/cmoviebitmap.h
controls/cmoviebutton.cpp
controls/cmoviebutton.h
controls/coptionmenu.cpp
controls/coptionmenu.h
controls/cparamdisplay.cpp
controls/cparamdisplay.h
controls/cscrollbar.cpp
controls/cscrollbar.h
controls/csearchtextedit.cpp
controls/csearchtextedit.h
controls/csegmentbutton.cpp
controls/csegmentbutton.h
controls/cslider.cpp
controls/cslider.h
controls/cspecialdigit.cpp
controls/cspecialdigit.h
controls/csplashscreen.cpp
controls/csplashscreen.h
controls/cstringlist.cpp
controls/cstringlist.h
controls/cswitch.cpp
controls/cswitch.h
controls/ctextedit.cpp
controls/ctextedit.h
controls/ctextlabel.cpp
controls/ctextlabel.h
controls/cvumeter.cpp
controls/cvumeter.h
controls/cxypad.cpp
controls/cxypad.h
controls/icommandmenuitemtarget.h
controls/icontrollistener.h
controls/ioptionmenulistener.h
controls/itexteditlistener.h
controls/itextlabellistener.h
copenglview.cpp
copenglview.h
cpoint.cpp
cpoint.h
crect.cpp
crect.h
cresourcedescription.h
crowcolumnview.cpp
crowcolumnview.h
cscrollview.cpp
cscrollview.h
cshadowviewcontainer.cpp
cshadowviewcontainer.h
csplitview.cpp
csplitview.h
cstring.cpp
cstring.h
ctabview.cpp
ctabview.h
ctooltipsupport.cpp
ctooltipsupport.h
cview.cpp
cview.h
cviewcontainer.cpp
cviewcontainer.h
cvstguitimer.cpp
cvstguitimer.h
dragging.h
dispatchlist.h
enumbitset.h
events.cpp
events.h
finally.h
genericstringlistdatabrowsersource.cpp
genericstringlistdatabrowsersource.h
idatabrowserdelegate.h
idatapackage.h
idependency.h
iexternalview.h
ifocusdrawing.h
iscalefactorchangedlistener.h
itouchevent.h
iviewlayouter.h
iviewlistener.h
malloc.h
optional.h
pixelbuffer.h
pixelbuffer.cpp
platform/iplatformbitmap.h
platform/iplatformgraphicsdevice.h
platform/iplatformfileselector.h
platform/iplatformfont.h
platform/iplatformframe.h
platform/iplatformframecallback.h
platform/iplatformgradient.h
platform/iplatformgraphicspath.h
platform/iplatformopenglview.h
platform/iplatformoptionmenu.h
platform/iplatformresourceinputstream.h
platform/iplatformstring.h
platform/iplatformtaskexecutor.h
platform/iplatformtextedit.h
platform/iplatformtextinputclient.h
platform/iplatformtimer.h
platform/iplatformviewlayer.h
platform/platformfactory.cpp
platform/platformfactory.h
platform/platformfwd.h
platform/platform_macos.h
platform/platform_linux.h
platform/platform_wayland.h
platform/platform_win32.h
platform/platform_x11.h
platform/std_unorderedmap.h
platform/common/fileresourceinputstream.cpp
platform/common/fileresourceinputstream.h
platform/common/genericoptionmenu.cpp
platform/common/genericoptionmenu.h
platform/common/generictextedit.cpp
platform/common/generictextedit.h
platform/common/gradientbase.h
platform/common/threadpooltaskexecutor.h
viewlayouter/autosizeviewlayouter.cpp
viewlayouter/autosizeviewlayouter.h
viewlayouter/baseviewlayouter.h
viewlayouter/gridlayouter.cpp
viewlayouter/gridlayouter.h
viewlayouter/noviewlayouter.h
tasks.cpp
tasks.h
vstguibase.h
vstguidebug.cpp
vstguidebug.h
vstguifwd.h
vstguiinit.cpp
vstguiinit.h
vstkeycode.h
)
##########################################################################################
set(${target}_mac_sources
platform/mac/caviewlayer.h
platform/mac/caviewlayer.mm
platform/mac/cfontmac.h
platform/mac/cfontmac.mm
platform/mac/cgbitmap.cpp
platform/mac/cgbitmap.h
platform/mac/cocoa/autoreleasepool.h
platform/mac/cocoa/autoreleasepool.mm
platform/mac/cocoa/cocoahelpers.h
platform/mac/cocoa/cocoahelpers.mm
platform/mac/cocoa/cocoaopenglview.h
platform/mac/cocoa/cocoaopenglview.mm
platform/mac/cocoa/cocoatextedit.h
platform/mac/cocoa/cocoatextedit.mm
platform/mac/cocoa/nsviewdraggingsession.h
platform/mac/cocoa/nsviewdraggingsession.mm
platform/mac/cocoa/nsviewframe.h
platform/mac/cocoa/nsviewframe.mm
platform/mac/cocoa/nsviewoptionmenu.h
platform/mac/cocoa/nsviewoptionmenu.mm
platform/mac/cocoa/objcclassbuilder.h
platform/mac/coregraphicsdevicecontext.h
platform/mac/coregraphicsdevicecontext.mm
platform/mac/macclipboard.h
platform/mac/macclipboard.mm
platform/mac/macfactory.h
platform/mac/macfactory.mm
platform/mac/macfileselector.mm
platform/mac/macfileselector.h
platform/mac/macglobals.cpp
platform/mac/macglobals.h
platform/mac/macstring.h
platform/mac/macstring.mm
platform/mac/mactaskexecutor.mm
platform/mac/mactaskexecutor.h
platform/mac/mactimer.cpp
platform/mac/mactimer.h
platform/mac/quartzgraphicspath.cpp
platform/mac/quartzgraphicspath.h
platform/win32
platform/linux
platform/mac/ios
../doxygen
)
##########################################################################################
set(${target}_win32_sources
platform/win32/direct2d/d2d.h
platform/win32/direct2d/d2dbitmap.cpp
platform/win32/direct2d/d2dbitmap.h
platform/win32/direct2d/d2dbitmapcache.cpp
platform/win32/direct2d/d2dbitmapcache.h
platform/win32/direct2d/d2dfont.cpp
platform/win32/direct2d/d2dfont.h
platform/win32/direct2d/d2dgradient.cpp
platform/win32/direct2d/d2dgradient.h
platform/win32/direct2d/d2dgraphicscontext.cpp
platform/win32/direct2d/d2dgraphicscontext.h
platform/win32/direct2d/d2dgraphicspath.cpp
platform/win32/direct2d/d2dgraphicspath.h
platform/win32/win32bitmapbase.h
platform/win32/win32taskexecutor.cpp
platform/win32/win32taskexecutor.h
platform/win32/win32dll.h
platform/win32/win32datapackage.cpp
platform/win32/win32datapackage.h
platform/win32/win32directcomposition.cpp
platform/win32/win32directcomposition.h
platform/win32/win32dragging.cpp
platform/win32/win32dragging.h
platform/win32/win32factory.cpp
platform/win32/win32factory.h
platform/win32/win32frame.cpp
platform/win32/win32frame.h
platform/win32/win32openglview.cpp
platform/win32/win32openglview.h
platform/win32/win32optionmenu.cpp
platform/win32/win32optionmenu.h
platform/win32/win32resourcestream.cpp
platform/win32/win32resourcestream.h
platform/win32/win32support.cpp
platform/win32/win32support.h
platform/win32/win32textedit.cpp
platform/win32/win32textedit.h
platform/win32/win32viewlayer.cpp
platform/win32/win32viewlayer.h
platform/win32/winfileselector.cpp
platform/win32/winfileselector.h
platform/win32/winstring.cpp
platform/win32/winstring.h
platform/win32/wintimer.cpp
platform/win32/wintimer.h
)
##########################################################################################
set(${target}_linux_sources
platform/linux/cairobitmap.cpp
platform/linux/cairobitmap.h
platform/linux/cairofont.cpp
platform/linux/cairofont.h
platform/linux/cairogradient.cpp
platform/linux/cairogradient.h
platform/linux/cairographicscontext.cpp
platform/linux/cairographicscontext.h
platform/linux/cairopath.cpp
platform/linux/cairopath.h
platform/linux/cairoutils.h
platform/linux/linuxstring.cpp
platform/linux/linuxstring.h
platform/linux/x11dragging.cpp
platform/linux/x11dragging.h
platform/linux/x11fileselector.cpp
platform/linux/x11fileselector.h
platform/linux/x11frame.cpp
platform/linux/x11frame.h
platform/linux/x11platform.cpp
platform/linux/x11platform.h
platform/linux/x11timer.cpp
platform/linux/x11timer.h
platform/linux/x11utils.cpp
platform/linux/x11utils.h
platform/linux/linuxfactory.cpp
platform/linux/linuxfactory.h
platform/linux/linuxtaskexecutor.cpp
platform/linux/linuxtaskexecutor.h
)
if(LINUX AND VSTGUI_ENABLE_WAYLAND_SUPPORT)
set(${target}_linux_sources
${${target}_linux_sources}
platform/linux/waylandframe.cpp
platform/linux/waylandframe.h
platform/linux/waylandplatform.cpp
platform/linux/waylandplatform.h
platform/linux/waylandutils.cpp
platform/linux/waylandutils.h
platform/linux/waylandclientcontext.cpp
platform/linux/waylandclientcontext.h
)
endif()
##########################################################################################
if(CMAKE_HOST_APPLE)
set(${target}_sources ${${target}_common_sources} ${${target}_mac_sources})
endif()
##########################################################################################
if(WIN32)
set(${target}_sources ${${target}_common_sources} ${${target}_win32_sources})
endif()
##########################################################################################
# Linux
if(LINUX)
set(${target}_sources ${${target}_common_sources} ${${target}_linux_sources})
endif()
##########################################################################################
add_library(${target} STATIC ${${target}_sources})
target_precompile_headers(${target} PRIVATE cviewcontainer.h cdrawcontext.h)
target_compile_definitions(${target} ${VSTGUI_COMPILE_DEFINITIONS})
vstgui_set_cxx_version(${target} ${VSTGUI_CXX_VERSION})
vstgui_source_group_by_folder(${target})
if(LINUX AND VSTGUI_ENABLE_WAYLAND_SUPPORT)
# Fetch wayland-server-delegate to get iwaylandclientcontext.h
include (FetchContent)
FetchContent_Declare(
ccl_wayland_server_delegate
GIT_REPOSITORY https://github.com/cclsoftware/wayland-server-delegate.git
GIT_TAG 86c32c61f0c861448635c041da5c343ce097ccff
)
FetchContent_MakeAvailable(ccl_wayland_server_delegate)
target_include_directories(${target}
PRIVATE
${ccl_wayland_server_delegate_SOURCE_DIR}
)
target_link_libraries(${target} PRIVATE ${WAYLAND_LIBRARIES})
target_compile_definitions(${target} PRIVATE VSTGUI_ENABLE_WAYLAND_SUPPORT=1)
endif()
if(LINUX)
target_include_directories(${target} PRIVATE ${X11_INCLUDE_DIR})
target_include_directories(${target} PRIVATE ${FREETYPE_INCLUDE_DIRS})
target_include_directories(${target} PRIVATE ${GLIB_INCLUDE_DIRS})
target_include_directories(${target} PRIVATE ${CAIRO_INCLUDE_DIRS})
target_include_directories(${target} PRIVATE ${PANGO_INCLUDE_DIRS})
target_include_directories(${target} PRIVATE ${FONTCONFIG_INCLUDE_DIRS})
target_link_libraries(${target} PRIVATE ${LINUX_LIBRARIES})
endif()
if(CMAKE_HOST_APPLE)
target_compile_options(${target} PRIVATE -Wall -Werror)
set(PLATFORM_LIBRARIES
"-framework Cocoa"
"-framework QuartzCore"
"-framework Accelerate"
)
if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 11.0)
target_compile_definitions(${target} PRIVATE "VSTGUI_USE_OBJC_UTTYPE")
set(PLATFORM_LIBRARIES
${PLATFORM_LIBRARIES}
"-framework UniformTypeIdentifiers"
)
endif()
if(VSTGUI_ENABLE_OPENGL_SUPPORT)
set(PLATFORM_LIBRARIES
${PLATFORM_LIBRARIES}
"-framework OpenGL"
)
endif()
target_link_libraries(${target}
PUBLIC
${PLATFORM_LIBRARIES}
)
endif()
+97
View File
@@ -0,0 +1,97 @@
// 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
#pragma once
#include "optional.h"
#include "vstguidebug.h"
#include <cstdint>
#include <algorithm>
//------------------------------------------------------------------------
namespace VSTGUI {
//------------------------------------------------------------------------
/** Returns the index of the value */
template <typename Iter, typename Type, typename ResultType = int32_t>
Optional<ResultType> indexOf (Iter first, Iter last, const Type& value)
{
auto it = std::find (first, last, value);
if (it == last)
return {};
return {static_cast<ResultType> (std::distance (first, it))};
}
//------------------------------------------------------------------------
/** Returns the index of the element for which predicate p returns true */
template <typename Iter, typename Proc, typename ResultType = int32_t>
Optional<ResultType> indexOfTest (Iter first, Iter last, Proc p)
{
auto it = std::find_if (first, last, p);
if (it == last)
return {};
return {static_cast<ResultType> (std::distance (first, it))};
}
//------------------------------------------------------------------------
/** Returns the value clamped to min and max */
template <typename T>
T clamp (T value, T min, T max)
{
return std::min (max, std::max (value, min));
}
//------------------------------------------------------------------------
/** Returns the value clamped to zero and one */
template <typename T>
T clampNorm (T value)
{
static_assert (std::is_floating_point<T>::value, "Only floating point types allowed");
return clamp (value, static_cast<T> (0), static_cast<T> (1));
}
//------------------------------------------------------------------------
/** Returns the value projected lineary between stepOffset and stepOffset + steps */
template<typename NormT, typename StepT = int32_t>
StepT normalizedToSteps (NormT value, StepT numSteps, StepT stepStart = static_cast<StepT> (0))
{
static_assert (std::is_integral<StepT>::value, "Step type must be integral");
vstgui_assert (value >= 0. && value <= 1., "Only normalized values are allowed");
return std::min<StepT> (numSteps, static_cast<StepT> ((numSteps + 1) * value)) + stepStart;
}
//------------------------------------------------------------------------
/** Returns the normalized value from the step value */
template<typename NormT, typename StepValueT, typename StepT>
NormT stepsToNormalized (StepValueT value, StepT steps, StepT stepOffset = static_cast<StepT> (0))
{
static_assert (std::is_integral<StepT>::value, "Step type must be integral");
vstgui_assert ((value - stepOffset) <= steps, "Value must be smaller or equal then steps");
return static_cast<NormT> (value - stepOffset) / static_cast<NormT> (steps);
}
//------------------------------------------------------------------------
/** Returns the normalized value from a plain one */
template<typename NormT, typename PlainT>
NormT plainToNormalized (PlainT value, PlainT minValue, PlainT maxValue)
{
static_assert (std::is_floating_point<NormT>::value,
"Only floating point types for NormT allowed");
vstgui_assert (maxValue - minValue != 0., "min and max value must be different");
return (value - minValue) / static_cast<NormT> (maxValue - minValue);
}
//------------------------------------------------------------------------
/** Returns the plain value from a normalized one */
template<typename PlainT, typename NormT>
PlainT normalizedToPlain (NormT value, PlainT minValue, PlainT maxValue)
{
static_assert (std::is_floating_point<NormT>::value,
"Only floating point types for NormT allowed");
vstgui_assert (maxValue - minValue != 0., "min and max value must be different");
return static_cast<PlainT> ((maxValue - minValue) * value + minValue);
}
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,387 @@
// 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 "animations.h"
#include "../cview.h"
#include "../cframe.h"
#include "../controls/ccontrol.h"
#include <cassert>
#include <cmath>
namespace VSTGUI {
namespace Animation {
//------------------------------------------------------------------------
/*! @defgroup AnimationTargets Animation Targets
* @ingroup animation
*/
//------------------------------------------------------------------------
/** @class AlphaValueAnimation
see @ref page_animation Support */
//-----------------------------------------------------------------------------
AlphaValueAnimation::AlphaValueAnimation (float endValue, bool forceEndValueOnFinish)
: startValue (0.f)
, endValue (endValue)
, forceEndValueOnFinish (forceEndValueOnFinish)
{
}
//-----------------------------------------------------------------------------
void AlphaValueAnimation::animationStart (CView* view, IdStringPtr name)
{
startValue = view->getAlphaValue ();
}
//-----------------------------------------------------------------------------
void AlphaValueAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
float alpha = startValue + (endValue - startValue) * pos;
view->setAlphaValue (alpha);
}
//-----------------------------------------------------------------------------
void AlphaValueAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
if (!wasCanceled || forceEndValueOnFinish)
view->setAlphaValue (endValue);
}
//-----------------------------------------------------------------------------
/** @class ViewSizeAnimation
see @ref page_animation Support */
//-----------------------------------------------------------------------------
ViewSizeAnimation::ViewSizeAnimation (const CRect& inNewRect, bool forceEndValueOnFinish)
: newRect (inNewRect)
, forceEndValueOnFinish (forceEndValueOnFinish)
{
}
//-----------------------------------------------------------------------------
void ViewSizeAnimation::animationStart (CView* view, IdStringPtr name)
{
startRect = view->getViewSize ();
}
//-----------------------------------------------------------------------------
void ViewSizeAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
if (!wasCanceled || forceEndValueOnFinish)
{
if (view->getViewSize () != newRect)
{
view->invalid ();
view->setViewSize (newRect);
view->setMouseableArea (view->getViewSize ());
view->invalid ();
}
}
}
//-----------------------------------------------------------------------------
void ViewSizeAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
CRect r;
r.left = (int32_t)(startRect.left + ((newRect.left - startRect.left) * pos));
r.right = (int32_t)(startRect.right + ((newRect.right - startRect.right) * pos));
r.top = (int32_t)(startRect.top + ((newRect.top - startRect.top) * pos));
r.bottom = (int32_t)(startRect.bottom + ((newRect.bottom - startRect.bottom) * pos));
if (view->getViewSize () != r)
{
view->invalid ();
view->setViewSize (r);
view->setMouseableArea (view->getViewSize ());
view->invalid ();
}
}
//-----------------------------------------------------------------------------
/** @class ExchangeViewAnimation
see @ref page_animation Support */
//-----------------------------------------------------------------------------
ExchangeViewAnimation::ExchangeViewAnimation (CView* oldView, CView* newView, AnimationStyle style)
: newView (newView)
, viewToRemove (oldView)
, style (style)
{
vstgui_assert (newView->isAttached () == false);
vstgui_assert (viewToRemove->isAttached ());
if (auto parent = viewToRemove->getParentView ()->asViewContainer ())
parent->addView (newView);
init ();
}
//-----------------------------------------------------------------------------
ExchangeViewAnimation::~ExchangeViewAnimation () noexcept
{
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::updateViewSize (CView* view, const CRect& rect)
{
view->invalid ();
view->setViewSize (rect);
view->setMouseableArea (rect);
view->invalid ();
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::init ()
{
if (style == kAlphaValueFade)
{
oldViewAlphaValueStart = viewToRemove->getAlphaValue ();
newViewAlphaValueEnd = newView->getAlphaValue ();
newView->setAlphaValue (0.f);
}
else
{
destinationRect = viewToRemove->getViewSize ();
switch (style)
{
case kAlphaValueFade: break;
case kPushInFromLeft:
{
doPushInFromLeft (0.f);
break;
}
case kPushInFromRight:
{
doPushInFromRight (0.f);
break;
}
case kPushInFromTop:
{
doPushInFromTop (0.f);
break;
}
case kPushInFromBottom:
{
doPushInFromBottom (0.f);
break;
}
case kPushInOutFromLeft:
{
doPushInOutFromLeft (0.f);
break;
}
case kPushInOutFromRight:
{
doPushInOutFromRight (0.f);
break;
}
}
}
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doAlphaFade (float pos)
{
float alpha = oldViewAlphaValueStart - (oldViewAlphaValueStart * pos);
viewToRemove->setAlphaValue (alpha);
alpha = newViewAlphaValueEnd * pos;
newView->setAlphaValue (alpha);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInFromLeft (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord leftOrigin = destinationRect.left;
CCoord offset = viewSize.getWidth () * (1.f - pos);
viewSize.offset (-viewSize.left, 0);
viewSize.offset (leftOrigin - offset, 0);
updateViewSize (newView, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInFromRight (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord rightOrigin = destinationRect.left + destinationRect.getWidth ();
CCoord offset = viewSize.getWidth () * pos;
viewSize.offset (-viewSize.left, 0);
viewSize.offset (rightOrigin - offset, 0);
updateViewSize (newView, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInFromTop (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord topOrigin = destinationRect.top;
CCoord offset = viewSize.getHeight () * (1.f - pos);
viewSize.offset (0, -viewSize.top);
viewSize.offset (0, topOrigin - offset);
updateViewSize (newView, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInFromBottom (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord bottomOrigin = destinationRect.top + destinationRect.getHeight ();
CCoord offset = viewSize.getHeight () * pos;
viewSize.offset (0, -viewSize.top);
viewSize.offset (0, bottomOrigin - offset);
updateViewSize (newView, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInOutFromLeft (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord offset = viewSize.getWidth () * (1.f - pos);
viewSize.offset (-viewSize.left, 0);
viewSize.offset (destinationRect.left - offset, 0);
updateViewSize (newView, viewSize);
offset = viewToRemove->getWidth () * pos;
viewSize = destinationRect;
viewSize.offset (offset, 0);
updateViewSize (viewToRemove, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInOutFromRight (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord offset = viewSize.getWidth () * pos;
viewSize.offset (-viewSize.left, 0);
viewSize.offset ((destinationRect.left + destinationRect.getWidth ()) - offset, 0);
updateViewSize (newView, viewSize);
offset = viewToRemove->getWidth () * pos;
viewSize = destinationRect;
viewSize.offset (-offset, 0);
updateViewSize (viewToRemove, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::animationStart (CView* view, IdStringPtr name)
{
#if DEBUG
CViewContainer* parent = viewToRemove->getParentView ()->asViewContainer ();
vstgui_assert (view == parent);
#endif
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
switch (style)
{
case kAlphaValueFade:
{
doAlphaFade (pos);
break;
}
case kPushInFromLeft:
{
doPushInFromLeft (pos);
break;
}
case kPushInFromRight:
{
doPushInFromRight (pos);
break;
}
case kPushInFromTop:
{
doPushInFromTop (pos);
break;
}
case kPushInFromBottom:
{
doPushInFromBottom (pos);
break;
}
case kPushInOutFromLeft:
{
doPushInOutFromLeft (pos);
break;
}
case kPushInOutFromRight:
{
doPushInOutFromRight (pos);
break;
}
}
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
animationTick (nullptr, nullptr, 1.f);
if (auto viewContainer = viewToRemove->getParentView ()->asViewContainer ())
{
viewContainer->removeView (viewToRemove);
}
}
//-----------------------------------------------------------------------------
/** @class ControlValueAnimation
see @ref page_animation Support */
//-----------------------------------------------------------------------------
ControlValueAnimation::ControlValueAnimation (float endValue, bool forceEndValueOnFinish)
: startValue (0.f)
, endValue (endValue)
, forceEndValueOnFinish (forceEndValueOnFinish)
{
}
//-----------------------------------------------------------------------------
void ControlValueAnimation::animationStart (CView* view, IdStringPtr name)
{
auto* control = dynamic_cast<CControl*> (view);
if (control)
startValue = control->getValue ();
}
//-----------------------------------------------------------------------------
void ControlValueAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
auto* control = dynamic_cast<CControl*> (view);
if (control)
{
float value = startValue + (endValue - startValue) * pos;
control->setValue (value);
if (control->isDirty ())
control->invalid ();
}
}
//-----------------------------------------------------------------------------
void ControlValueAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
auto* control = dynamic_cast<CControl*> (view);
if (control)
{
if (!wasCanceled || forceEndValueOnFinish)
control->setValue (endValue);
}
}
//------------------------------------------------------------------------
FuncAnimation::FuncAnimation (StartFunc&& start, TickFunc&& tick, FinishedFunc&& finished)
: start (std::move (start)), tick (std::move (tick)), finished (std::move (finished))
{
}
//------------------------------------------------------------------------
void FuncAnimation::animationStart (CView* view, IdStringPtr name) { start (view, name); }
//------------------------------------------------------------------------
void FuncAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
tick (view, name, pos);
}
//------------------------------------------------------------------------
void FuncAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
finished (view, name, wasCanceled);
}
}} // namespaces
@@ -0,0 +1,144 @@
// 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
#pragma once
#include "../vstguifwd.h"
#include "ianimationtarget.h"
#include "../crect.h"
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @brief animates the alpha value of the view
/// @ingroup AnimationTargets
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class AlphaValueAnimation : public IAnimationTarget, public NonAtomicReferenceCounted
{
public:
AlphaValueAnimation (float endValue, bool forceEndValueOnFinish = false);
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
protected:
float startValue;
float endValue;
bool forceEndValueOnFinish;
};
//-----------------------------------------------------------------------------
/// @brief animates the view size of the view
/// @ingroup AnimationTargets
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class ViewSizeAnimation : public IAnimationTarget, public NonAtomicReferenceCounted
{
public:
ViewSizeAnimation (const CRect& newRect, bool forceEndValueOnFinish = false);
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
protected:
CRect startRect;
CRect newRect;
bool forceEndValueOnFinish;
};
//-----------------------------------------------------------------------------
/// @brief exchange a view by another view with an animation
/// @ingroup AnimationTargets
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class ExchangeViewAnimation : public IAnimationTarget, public NonAtomicReferenceCounted
{
public:
enum AnimationStyle {
kAlphaValueFade = 0,
kPushInFromLeft,
kPushInFromRight,
kPushInFromTop,
kPushInFromBottom,
kPushInOutFromLeft,
kPushInOutFromRight
};
/** oldView must be a subview of the animation view */
ExchangeViewAnimation (CView* oldView, CView* newView, AnimationStyle style = kAlphaValueFade);
~ExchangeViewAnimation () noexcept override;
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
protected:
void init ();
void doAlphaFade (float pos);
void doPushInFromLeft (float pos);
void doPushInFromRight (float pos);
void doPushInFromTop (float pos);
void doPushInFromBottom (float pos);
void doPushInOutFromLeft (float pos);
void doPushInOutFromRight (float pos);
void updateViewSize (CView* view, const CRect& rect);
SharedPointer<CView> newView;
SharedPointer<CView> viewToRemove;
AnimationStyle style;
float newViewAlphaValueEnd;
float oldViewAlphaValueStart;
CRect destinationRect;
};
//-----------------------------------------------------------------------------
/// @brief animates the value of a CControl
/// @ingroup AnimationTargets
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class ControlValueAnimation : public IAnimationTarget, public NonAtomicReferenceCounted
{
public:
ControlValueAnimation (float endValue, bool forceEndValueOnFinish = false);
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
protected:
float startValue;
float endValue;
bool forceEndValueOnFinish;
};
//------------------------------------------------------------------------
/// @brief animation via custom functions
/// @ingroup AnimationTargets
/// @ingroup new_in_4_14
//------------------------------------------------------------------------
class FuncAnimation : public IAnimationTarget,
public NonAtomicReferenceCounted
{
public:
using StartFunc = std::function<void (CView*, IdStringPtr)>;
using TickFunc = std::function<void (CView*, IdStringPtr, float)>;
using FinishedFunc = std::function<void (CView*, IdStringPtr, bool)>;
FuncAnimation (StartFunc&& start, TickFunc&& tick, FinishedFunc&& finished);
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
private:
StartFunc start;
TickFunc tick;
FinishedFunc finished;
};
} // Animation
} // VSTGUI
@@ -0,0 +1,374 @@
// 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
/**
@page page_animation Animations
VSTGUI version 4 adds simple to use view animation support.
The source can be found under /lib/animation/
@section the_animator The Animator
Every @link VSTGUI::CFrame::getAnimator CFrame @endlink object can have one @link VSTGUI::Animation::Animator Animator @endlink object which runs animations at 60 Hz.
The animator is responsible for running animations.
You can add and remove animations.
Animations are identified by a view and a name.
To add an animation you just call @link VSTGUI::CView::addAnimation CView::addAnimation (name, target, timing)@endlink.
The animation will start immediately and will automatically be removed if it has finished.
If you want to stop it before it has finished you can use @link VSTGUI::CView::removeAnimation CView::removeAnimation (name)@endlink.
You can also stop all animations for a view with @link VSTGUI::CView::removeAllAnimations CView::removeAllAnimations ()@endlink.
The animator is the owner of the target and timing function objects and will destroy these objects when the animation has finished.
This means that the animator will call delete on these objects or if they are inherited from CBaseObject it will call forget() on them.
@section the_animation The Animation
An animation is made up by an @link VSTGUI::Animation::IAnimationTarget IAnimationTarget @endlink and an @link VSTGUI::Animation::ITimingFunction ITimingFunction @endlink object.
@subsection animation_target The Animation Target
The animation target is responsible for changing the view from one state to another state.
The animation target interface consists of 3 methods:
- @link VSTGUI::Animation::IAnimationTarget::animationStart animationStart (view, name) @endlink
- @link VSTGUI::Animation::IAnimationTarget::animationTick animationTick (view, name, pos) @endlink
- @link VSTGUI::Animation::IAnimationTarget::animationFinished animationFinished (view, name, wasCanceled) @endlink
All these methods have the view and the animation name as arguments to identify the animation within the target.
The animationTick method in addition has the normalized animation position as argument and the animationFinished method has a bool argument indicating if the animation was canceled.
see @link AnimationTargets included animation target classes @endlink
@subsection animation_timing The Animation Timing Function
the animation timing function maps elapsed time to a normalized position.
see @link AnimationTimingFunctions included animation timing function classes @endlink
@section simple_example Simple Usage Example
In this example the custom view animates it's alpha value when the mouse moves inside or outside the view.
@code
using namespace VSTGUI::Animation;
class MyView : public CView
{
public:
MyView (const CRect& r) : CView (r) { setAlphaValue (0.5f); }
CMouseEventResult onMouseEntered (CPoint &where, const CButtonState& buttons)
{
// this adds an animation which takes 200 ms to make a linear alpha fade from the current value to 1
addAnimation ("AlphaValueAnimation", new AlphaValueAnimation (1.f), new LinearTimingFunction (200));
return kMouseEventHandled;
}
CMouseEventResult onMouseExited (CPoint &where, const CButtonState& buttons)
{
// this adds an animation which takes 200 ms to make a linear alpha fade from the current value to 0.5
addAnimation ("AlphaValueAnimation", new AlphaValueAnimation (0.5f), new LinearTimingFunction (200));
return kMouseEventHandled;
}
void draw (CDrawContext* context)
{
// ... any drawing code here
}
};
@endcode
*/
//------------------------------------------------------------------------
/*! @defgroup animation Animation
see @ref page_animation
*/
//-----------------------------------------------------------------------------
#include "animator.h"
#include "ianimationtarget.h"
#include "itimingfunction.h"
#include "../cvstguitimer.h"
#include "../cview.h"
#include "../dispatchlist.h"
#include "../platform/platformfactory.h"
#include <list>
#define DEBUG_LOG 0 // DEBUG
namespace VSTGUI {
namespace Animation {
///@cond ignore
namespace Detail {
//-----------------------------------------------------------------------------
class Timer : public NonAtomicReferenceCounted
{
public:
static void addAnimator (Animator* animator)
{
getInstance ()->animators.emplace_back (animator);
#if DEBUG_LOG
DebugPrint ("Animator added: %p\n", animator);
#endif
}
static void removeAnimator (Animator* animator)
{
if (gInstance)
{
if (getInstance ()->inTimer)
{
gInstance->toRemove.emplace_back (animator);
}
else
{
#if DEBUG_LOG
DebugPrint ("Animator removed: %p\n", animator);
#endif
gInstance->animators.remove (animator);
if (gInstance->animators.empty ())
{
gInstance->forget ();
gInstance = nullptr;
}
}
}
}
protected:
static Timer* getInstance ()
{
if (gInstance == nullptr)
gInstance = new Timer;
return gInstance;
}
Timer ()
: inTimer (false)
{
#if DEBUG_LOG
DebugPrint ("Animation timer started\n");
#endif
timer = new CVSTGUITimer ([this] (CVSTGUITimer*) {
onTimer ();
}, 1000/60); // 60 Hz
}
~Timer () noexcept override
{
#if DEBUG_LOG
DebugPrint ("Animation timer stopped\n");
#endif
timer->forget ();
gInstance = nullptr;
}
void onTimer ()
{
inTimer = true;
auto guard = shared (this);
#if DEBUG_LOG
DebugPrint ("Current Animators : %d\n", animators.size ());
#endif
for (auto& animator : animators)
animator->onTimer ();
inTimer = false;
for (auto& animator : toRemove)
removeAnimator (animator);
toRemove.clear ();
}
CVSTGUITimer* timer;
using Animators = std::list<Animator*>;
Animators animators;
Animators toRemove;
bool inTimer;
static Timer* gInstance;
};
Timer* Timer::gInstance = nullptr;
//-----------------------------------------------------------------------------
class Animation : public NonAtomicReferenceCounted
{
public:
Animation (CView* view, const std::string& name, IAnimationTarget* at, ITimingFunction* t,
DoneFunction&& notification, bool notifyOnCancel);
~Animation () noexcept override;
std::string name;
SharedPointer<CView> view;
IAnimationTarget* animationTarget;
ITimingFunction* timingFunction;
DoneFunction notification;
uint64_t startTime {0};
float lastPos {-1.};
bool done {false};
bool notifyOnCancel;
};
//-----------------------------------------------------------------------------
Animation::Animation (CView* view, const std::string& name, IAnimationTarget* at,
ITimingFunction* t, DoneFunction&& notification, bool notifyOnCancel)
: name (name)
, view (view)
, animationTarget (at)
, timingFunction (t)
, notification (std::move (notification))
, notifyOnCancel (notifyOnCancel)
{
}
//-----------------------------------------------------------------------------
Animation::~Animation () noexcept
{
if (notification)
notification (view, name.c_str (), animationTarget);
if (auto obj = dynamic_cast<IReference*> (animationTarget))
obj->forget ();
else
delete animationTarget;
if (auto obj = dynamic_cast<IReference*> (timingFunction))
obj->forget ();
else
delete timingFunction;
}
} // Detail
//-----------------------------------------------------------------------------
struct Animator::Impl
{
DispatchList<SharedPointer<Detail::Animation>> animations;
};
///@endcond
//-----------------------------------------------------------------------------
Animator::Animator ()
{
pImpl = std::unique_ptr<Impl> (new Impl ());
}
//-----------------------------------------------------------------------------
Animator::~Animator () noexcept
{
Detail::Timer::removeAnimator (this);
}
//-----------------------------------------------------------------------------
void Animator::addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target,
ITimingFunction* timingFunction, DoneFunction notification,
bool notifyOnCancel)
{
if (pImpl->animations.empty ())
Detail::Timer::addAnimator (this);
removeAnimation (view, name);
pImpl->animations.add (makeOwned<Detail::Animation> (view, name, target, timingFunction,
std::move (notification), notifyOnCancel));
#if DEBUG_LOG
DebugPrint ("new animation added: %p - %s\n", view, name);
#endif
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//-----------------------------------------------------------------------------
void Animator::addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target, ITimingFunction* timingFunction, CBaseObject* notificationObject)
{
DoneFunction notification;
if (notificationObject)
{
SharedPointer<CBaseObject> nObj (notificationObject);
notification = [nObj] (CView* view, const IdStringPtr name, IAnimationTarget* target) {
FinishedMessage fmsg (view, name, target);
nObj->notify (&fmsg, kMsgAnimationFinished);
};
}
addAnimation (view, name, target, timingFunction, std::move (notification));
}
#endif
//-----------------------------------------------------------------------------
void Animator::removeAnimation (CView* view, IdStringPtr name)
{
pImpl->animations.forEach ([&] (const SharedPointer<Detail::Animation>& animation) {
if (animation->view == view && animation->name == name)
{
#if DEBUG_LOG
DebugPrint ("animation removed: %p - %s\n", view, name);
#endif
if (animation->done == false)
{
animation->done = true;
animation->animationTarget->animationFinished (view, name, true);
}
if (!animation->notifyOnCancel)
animation->notification = nullptr;
pImpl->animations.remove (animation);
}
});
}
//-----------------------------------------------------------------------------
void Animator::removeAnimations (CView* view)
{
pImpl->animations.forEach ([&] (const SharedPointer<Detail::Animation>& animation) {
if (animation->view == view)
{
#if DEBUG_LOG
DebugPrint ("animation removed: %p - %s\n", view, animation->name.data ());
#endif
if (animation->done == false)
{
animation->done = true;
animation->animationTarget->animationFinished (view, animation->name.data (), true);
}
pImpl->animations.remove (animation);
}
});
}
//-----------------------------------------------------------------------------
void Animator::onTimer ()
{
auto selfGuard = shared (this);
auto currentTicks = getPlatformFactory ().getTicks ();
pImpl->animations.forEach ([&] (SharedPointer<Detail::Animation>& animation) {
if (animation->startTime == 0)
{
#if DEBUG_LOG
DebugPrint ("animation start: %p - %s\n", animation->view.cast<CView>(), animation->name.data ());
#endif
animation->animationTarget->animationStart (animation->view, animation->name.data ());
animation->startTime = currentTicks;
}
uint32_t time = static_cast<uint32_t> (currentTicks - animation->startTime);
float pos = animation->timingFunction->getPosition (time);
if (pos != animation->lastPos)
{
animation->animationTarget->animationTick (animation->view, animation->name.data (), pos);
animation->lastPos = pos;
}
if (animation->timingFunction->isDone (time))
{
animation->done = true;
animation->animationTarget->animationFinished (animation->view, animation->name.data (), false);
#if DEBUG_LOG
DebugPrint ("animation finished: %p - %s\n", animation->view.cast<CView>(), animation->name.data ());
#endif
pImpl->animations.remove (animation);
}
});
if (pImpl->animations.empty ())
Detail::Timer::removeAnimator (this);
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
IdStringPtr kMsgAnimationFinished = "kMsgAnimationFinished";
#endif
}} // namespaces
@@ -0,0 +1,95 @@
// 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
#pragma once
#include "../vstguifwd.h"
#include <string>
#include <functional>
#include <memory>
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @brief Animation runner
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class Animator : public NonAtomicReferenceCounted
{
public:
//-----------------------------------------------------------------------------
/// @name Adding and removing Animations
//-----------------------------------------------------------------------------
//@{
VSTGUI_DEPRECATED(
/** adds an animation.
Animation and timingFunction is now owned by the animator.
An already running animation for view with name will be canceled.
If a notificationObject is supplied, it will be notified when the animation has finished @see FinishedMessage.
*/
void addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target, ITimingFunction* timingFunction, CBaseObject* notificationObject);)
/** adds an animation.
Animation and timingFunction is now owned by the animator.
An already running animation for view with name will be canceled.
The notification function will be called when the animation has finished or on cancelation
of the animation if notifyOnCancel is true (new in 4.11)
*/
void addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target,
ITimingFunction* timingFunction, DoneFunction notification = nullptr,
bool notifyOnCancel = false);
/** removes an animation.
If animation has the IReference interface forget() will be called otherwise it is deleted.
The same will be done with the timingFunction.
*/
void removeAnimation (CView* view, IdStringPtr name);
/** removes all animations for view */
void removeAnimations (CView* view);
//@}
/// @cond ignore
Animator (); // do not use this, instead use CFrame::getAnimator()
void onTimer ();
protected:
~Animator () noexcept override;
struct Impl;
std::unique_ptr<Impl> pImpl;
/// @endcond
};
#if VSTGUI_ENABLE_DEPRECATED_METHODS
/** message sent to the notificationObject when the animation has finished, the sender parameter will be a FinishedMessage object. */
extern IdStringPtr kMsgAnimationFinished;
//-----------------------------------------------------------------------------
/// @brief Animation Finished Message Object
///
/// The FinishedMessage will be sent to the notificationObject when the animation has finished
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class FinishedMessage : public CBaseObject
{
public:
FinishedMessage (CView* view, const std::string& name, IAnimationTarget* target) : view (view), name (name), target (target) {}
CView* getView () const { return view; }
IdStringPtr getName () const { return name.c_str (); }
IAnimationTarget* getTarget () const { return target; }
CLASS_METHODS_NOCOPY(FinishedMessage, CBaseObject)
protected:
CView* view;
const std::string& name;
IAnimationTarget* target;
};
#endif
} // Animation
} // VSTGUI
@@ -0,0 +1,30 @@
// 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
#pragma once
#include "../vstguifwd.h"
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @brief Animation target interface
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class IAnimationTarget
{
public:
virtual ~IAnimationTarget () noexcept = default;
/** animation starts */
virtual void animationStart (CView* view, IdStringPtr name) = 0;
/** pos is a normalized value between zero and one */
virtual void animationTick (CView* view, IdStringPtr name, float pos) = 0;
/** animation ended */
virtual void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) = 0;
};
} // Animation
} // VSTGUI
@@ -0,0 +1,26 @@
// 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
#pragma once
#include "../vstguibase.h"
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @brief Animation timing function interface
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class ITimingFunction
{
public:
virtual ~ITimingFunction () noexcept = default;
virtual float getPosition (uint32_t milliseconds) = 0;
virtual bool isDone (uint32_t milliseconds) = 0;
};
} // Animation
} // VSTGUI
@@ -0,0 +1,219 @@
// 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 "timingfunctions.h"
#include "../vstguibase.h"
#include <cmath>
namespace VSTGUI {
namespace Animation {
//------------------------------------------------------------------------
/*! @defgroup AnimationTimingFunctions Animation Timing Functions
* @ingroup animation
*/
//------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
LinearTimingFunction::LinearTimingFunction (uint32_t length)
: TimingFunctionBase (length)
{
}
//-----------------------------------------------------------------------------
float LinearTimingFunction::getPosition (uint32_t milliseconds)
{
float pos = ((float)milliseconds) / ((float)length);
if (pos > 1.f)
pos = 1.f;
else if (pos < 0.f)
pos = 0.f;
return pos;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
PowerTimingFunction::PowerTimingFunction (uint32_t length, float factor)
: TimingFunctionBase (length)
, factor (factor)
{
}
//-----------------------------------------------------------------------------
float PowerTimingFunction::getPosition (uint32_t milliseconds)
{
float pos = ((float)milliseconds) / ((float)length);
pos = std::pow (pos, factor);
if (pos > 1.f)
pos = 1.f;
else if (pos < 0.f)
pos = 0.f;
return pos;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
InterpolationTimingFunction::InterpolationTimingFunction (uint32_t length, float startPos, float endPos)
: TimingFunctionBase (length)
{
addPoint (0.f, startPos);
addPoint (1.f, endPos);
}
//-----------------------------------------------------------------------------
void InterpolationTimingFunction::addPoint (float time, float pos)
{
points.emplace ((uint32_t)((float)getLength () * time), pos);
}
//-----------------------------------------------------------------------------
float InterpolationTimingFunction::getPosition (uint32_t milliseconds)
{
uint32_t prevTime = getLength ();
float prevPos = points[prevTime];
PointMap::reverse_iterator it = points.rbegin ();
while (it != points.rend ())
{
uint32_t time = it->first;
float pos = it->second;
if (time == milliseconds)
return pos;
else if (time <= milliseconds && prevTime > milliseconds)
{
double timePos = (double)(milliseconds - time) / (double)(prevTime - time);
return static_cast<float> (static_cast<double> (pos) + ((static_cast<double> (prevPos) - static_cast<double> (pos)) * timePos));
}
prevTime = time;
prevPos = pos;
++it;
}
return 1.f;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CubicBezierTimingFunction::CubicBezierTimingFunction (uint32_t milliseconds, CPoint p1, CPoint p2)
: TimingFunctionBase (milliseconds), p1 (p1), p2 (p2)
{
}
//-----------------------------------------------------------------------------
CPoint CubicBezierTimingFunction::lerp (CPoint p1, CPoint p2, float pos)
{
return p1 * (1.f - pos) + p2 * pos;
}
//-----------------------------------------------------------------------------
float CubicBezierTimingFunction::getPosition (uint32_t milliseconds)
{
constexpr CPoint p0 (0, 0);
constexpr CPoint p3 (1, 1);
auto t = static_cast<float> (milliseconds) / static_cast<float> (length);
auto a = lerp (p0, p1, t);
auto b = lerp (p1, p2, t);
auto c = lerp (p2, p3, t);
auto d = lerp (a, b, t);
auto e = lerp (b, c, t);
auto result = lerp (d, e, t).y;
return static_cast<float> (result);
}
//-----------------------------------------------------------------------------
CubicBezierTimingFunction CubicBezierTimingFunction::easy (uint32_t time)
{
return CubicBezierTimingFunction (time, CPoint (0.25, 0.1), CPoint (0.25, 1.));
}
//-----------------------------------------------------------------------------
CubicBezierTimingFunction CubicBezierTimingFunction::easyIn (uint32_t time)
{
return CubicBezierTimingFunction (time, CPoint (0.42, 0.), CPoint (1., 1.));
}
//-----------------------------------------------------------------------------
CubicBezierTimingFunction CubicBezierTimingFunction::easyOut (uint32_t time)
{
return CubicBezierTimingFunction (time, CPoint (0., 0.), CPoint (0.58, 1.));
}
//-----------------------------------------------------------------------------
CubicBezierTimingFunction CubicBezierTimingFunction::easyInOut (uint32_t time)
{
return CubicBezierTimingFunction (time, CPoint (0.42, 0.), CPoint (0.58, 1.));
}
//------------------------------------------------------------------------
CubicBezierTimingFunction* CubicBezierTimingFunction::make (Style style, uint32_t time)
{
using Func = CubicBezierTimingFunction;
switch (style)
{
case Easy:
return new CubicBezierTimingFunction (Func::easy (time));
case EasyIn:
return new CubicBezierTimingFunction (Func::easyIn (time));
case EasyOut:
return new CubicBezierTimingFunction (Func::easyOut (time));
case EasyInOut:
return new CubicBezierTimingFunction (Func::easyInOut (time));
}
return nullptr;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
RepeatTimingFunction::RepeatTimingFunction (TimingFunctionBase* tf, int32_t repeatCount, bool autoReverse)
: tf (tf)
, repeatCount (repeatCount)
, runCounter (0)
, autoReverse (autoReverse)
, isReverse (false)
{
}
//-----------------------------------------------------------------------------
RepeatTimingFunction::~RepeatTimingFunction () noexcept
{
auto obj = dynamic_cast<IReference*> (tf);
if (obj)
obj->forget ();
else
delete tf;
}
//-----------------------------------------------------------------------------
float RepeatTimingFunction::getPosition (uint32_t milliseconds)
{
if (runCounter > 0)
milliseconds -= tf->getLength () * runCounter;
float pos = tf->getPosition (milliseconds);
return isReverse ? 1.f - pos : pos;
}
//-----------------------------------------------------------------------------
bool RepeatTimingFunction::isDone (uint32_t milliseconds)
{
if (runCounter > 0)
milliseconds -= tf->getLength () * runCounter;
if (tf->isDone (milliseconds))
{
runCounter++;
if (autoReverse)
isReverse = !isReverse;
if ((uint64_t)runCounter >= (uint64_t)repeatCount)
return true;
}
return false;
}
}} // namespaces
@@ -0,0 +1,144 @@
// 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
#pragma once
#include "animator.h"
#include "itimingfunction.h"
#include "../cpoint.h"
#include <map>
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class TimingFunctionBase : public ITimingFunction
{
public:
explicit TimingFunctionBase (uint32_t length) : length (length) {}
TimingFunctionBase (const TimingFunctionBase&) = default;
TimingFunctionBase& operator= (const TimingFunctionBase&) = default;
uint32_t getLength () const { return length; }
bool isDone (uint32_t milliseconds) override { return milliseconds >= length; }
protected:
uint32_t length; // in milliseconds
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class LinearTimingFunction : public TimingFunctionBase
{
public:
explicit LinearTimingFunction (uint32_t length);
LinearTimingFunction (const LinearTimingFunction&) = default;
LinearTimingFunction& operator= (const LinearTimingFunction&) = default;
float getPosition (uint32_t milliseconds) override;
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class PowerTimingFunction : public TimingFunctionBase
{
public:
PowerTimingFunction (uint32_t length, float factor);
PowerTimingFunction (const PowerTimingFunction&) = default;
PowerTimingFunction& operator= (const PowerTimingFunction&) = default;
float getPosition (uint32_t milliseconds) override;
protected:
float factor;
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class InterpolationTimingFunction : public TimingFunctionBase
{
public:
InterpolationTimingFunction (uint32_t length, float startPos = 0.f, float endPos = 1.f);
InterpolationTimingFunction (const InterpolationTimingFunction&) = default;
InterpolationTimingFunction& operator= (const InterpolationTimingFunction&) = default;
/** both values are normalized ones */
void addPoint (float time, float pos);
float getPosition (uint32_t milliseconds) override;
protected:
using PointMap = std::map<uint32_t, float>;
PointMap points;
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_7
//-----------------------------------------------------------------------------
class CubicBezierTimingFunction : public TimingFunctionBase
{
public:
CubicBezierTimingFunction (uint32_t milliseconds, CPoint p1, CPoint p2);
CubicBezierTimingFunction (const CubicBezierTimingFunction&) = default;
CubicBezierTimingFunction (CubicBezierTimingFunction&&) = default;
CubicBezierTimingFunction& operator= (const CubicBezierTimingFunction&) = default;
CubicBezierTimingFunction& operator= (CubicBezierTimingFunction&&) = default;
float getPosition (uint32_t milliseconds) override;
// some common timings
static CubicBezierTimingFunction easy (uint32_t time);
static CubicBezierTimingFunction easyIn (uint32_t time);
static CubicBezierTimingFunction easyOut (uint32_t time);
static CubicBezierTimingFunction easyInOut (uint32_t time);
enum Style
{
Easy,
EasyIn,
EasyOut,
EasyInOut
};
static CubicBezierTimingFunction* make (Style style, uint32_t time);
private:
static CPoint lerp (CPoint p1, CPoint p2, float pos);
CPoint p1;
CPoint p2;
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class RepeatTimingFunction : public ITimingFunction
{
public:
RepeatTimingFunction (TimingFunctionBase* tf, int32_t repeatCount, bool autoReverse = true);
~RepeatTimingFunction () noexcept override;
float getPosition (uint32_t milliseconds) override;
bool isDone (uint32_t milliseconds) override;
protected:
TimingFunctionBase* tf;
int32_t repeatCount;
uint32_t runCounter;
bool autoReverse;
bool isReverse;
};
} // Animation
} // VSTGUI
+371
View File
@@ -0,0 +1,371 @@
// 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 "cbitmap.h"
#include "cdrawcontext.h"
#include "ccolor.h"
#include "algorithm.h"
#include "platform/iplatformbitmap.h"
#include "platform/platformfactory.h"
#include <cassert>
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CBitmap Implementation
//-----------------------------------------------------------------------------
/*! @class CBitmap
@section changes_version_4 Changes in 4.0
In Version 4.0 CBitmap was simplified. Previous versions supported drawing a color transparent of the bitmap. Since CBitmap supports
alpha drawing of bitmaps since some time, it's now the only way of drawing a bitmap with some parts transparent.
@section supported_file_formats Supported file formats
File format support is handled in a platform dependent way. On Windows GDI+ is used to import images. On Mac OS X CoreGraphics is used to import them.
For cross platform compatibility it is recommended to use PNG files.
@section loading Loading Bitmaps
You load a bitmap via a CResourceDescription which can hold a string or a number.
If you use names, you need to use the real filename with extension. Then it gets automaticly
loaded on Mac OS X out of the Resources folder of the vst bundle. On Windows you also specify the resource in the .rc file with the real filename.
@code
// using a number
1001 PNG DISCARDABLE "bmp01001.png"
// using a string
RealFileName.png PNG DISCARDABLE "RealFileName.png"
@endcode
@code
CBitmap* bitmap1 = new CBitmap (1001); // number
CBitmap* bitmap2 = new CBitmap ("RealFileName.png"); // string
@endcode
*/
//-----------------------------------------------------------------------------
CBitmap::CBitmap ()
{
}
//-----------------------------------------------------------------------------
CBitmap::CBitmap (const CResourceDescription& desc)
: resourceDesc (desc)
{
if (auto platformBitmap = getPlatformFactory ().createBitmap (desc))
bitmaps.emplace_back (platformBitmap);
}
//-----------------------------------------------------------------------------
CBitmap::CBitmap (CCoord width, CCoord height)
{
if (auto platformBitmap = getPlatformFactory ().createBitmap ({width, height}))
bitmaps.emplace_back (platformBitmap);
}
//------------------------------------------------------------------------
CBitmap::CBitmap (CPoint size, double scaleFactor)
{
size.x *= scaleFactor;
size.y *= scaleFactor;
size.makeIntegral ();
if (auto platformBitmap = getPlatformFactory ().createBitmap (size))
{
platformBitmap->setScaleFactor (scaleFactor);
bitmaps.emplace_back (platformBitmap);
}
}
//-----------------------------------------------------------------------------
CBitmap::CBitmap (const PlatformBitmapPtr& platformBitmap)
{
bitmaps.emplace_back (platformBitmap);
}
//-----------------------------------------------------------------------------
void CBitmap::draw (CDrawContext* context, const CRect& rect, const CPoint& offset, float alpha)
{
drawClipped (context, rect, [&] () {
context->drawBitmap (this, rect, offset, alpha);
});
}
//-----------------------------------------------------------------------------
CCoord CBitmap::getWidth () const
{
if (auto pb = getPlatformBitmap ())
return pb->getSize ().x / pb->getScaleFactor ();
return 0;
}
//-----------------------------------------------------------------------------
CCoord CBitmap::getHeight () const
{
if (auto pb = getPlatformBitmap ())
return pb->getSize ().y / pb->getScaleFactor ();
return 0;
}
//------------------------------------------------------------------------
CPoint CBitmap::getSize () const
{
CPoint p;
if (auto pb = getPlatformBitmap ())
{
auto scaleFactor = pb->getScaleFactor ();
p = pb->getSize ();
p.x /= scaleFactor;
p.y /= scaleFactor;
}
return p;
}
//-----------------------------------------------------------------------------
auto CBitmap::getPlatformBitmap () const -> PlatformBitmapPtr
{
return bitmaps.empty () ? nullptr : bitmaps[0];
}
//-----------------------------------------------------------------------------
void CBitmap::setPlatformBitmap (const PlatformBitmapPtr& bitmap)
{
if (bitmaps.empty ())
bitmaps.emplace_back (bitmap);
else
bitmaps[0] = bitmap;
}
//-----------------------------------------------------------------------------
bool CBitmap::addBitmap (const PlatformBitmapPtr& platformBitmap)
{
double scaleFactor = platformBitmap->getScaleFactor ();
CPoint size = getSize ();
CPoint bitmapSize = platformBitmap->getSize ();
bitmapSize.x /= scaleFactor;
bitmapSize.y /= scaleFactor;
if (size != bitmapSize)
{
vstgui_assert (size == bitmapSize, "wrong bitmap size");
return false;
}
for (const auto& bitmap : bitmaps)
{
if (bitmap->getScaleFactor () == scaleFactor || bitmap == platformBitmap)
{
vstgui_assert (bitmap->getScaleFactor () != scaleFactor && bitmap != platformBitmap);
return false;
}
}
bitmaps.emplace_back (platformBitmap);
return true;
}
//-----------------------------------------------------------------------------
auto CBitmap::getBestPlatformBitmapForScaleFactor (double scaleFactor) const -> PlatformBitmapPtr
{
if (bitmaps.empty ())
return nullptr;
auto bestBitmap = bitmaps[0];
double bestDiff = std::abs (scaleFactor - bestBitmap->getScaleFactor ());
for (const auto& bitmap : bitmaps)
{
if (bitmap->getScaleFactor () == scaleFactor)
return bitmap;
else if (std::abs (scaleFactor - bitmap->getScaleFactor ()) <= bestDiff && bitmap->getScaleFactor () > bestBitmap->getScaleFactor ())
{
bestBitmap = bitmap;
bestDiff = std::abs (scaleFactor - bitmap->getScaleFactor ());
}
}
return bestBitmap;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CMultiFrameBitmap::CMultiFrameBitmap (const CResourceDescription& desc,
CMultiFrameBitmapDescription multiFrameDesc)
: CBitmap (desc), description (multiFrameDesc)
{
}
//-----------------------------------------------------------------------------
bool CMultiFrameBitmap::setMultiFrameDesc (CMultiFrameBitmapDescription desc)
{
if (desc.frameSize.x * desc.framesPerRow > getSize ().x)
return false;
if (desc.frameSize.y * (desc.numFrames / desc.framesPerRow) > getSize ().y)
return false;
description = desc;
return true;
}
//-----------------------------------------------------------------------------
CMultiFrameBitmapDescription CMultiFrameBitmap::getMultiFrameDesc () const { return description; }
//-----------------------------------------------------------------------------
CPoint CMultiFrameBitmap::getFrameSize () const { return description.frameSize; }
//-----------------------------------------------------------------------------
uint16_t CMultiFrameBitmap::getNumFrames () const { return description.numFrames; }
//-----------------------------------------------------------------------------
uint16_t CMultiFrameBitmap::getNumFramesPerRow () const { return description.framesPerRow; }
//-----------------------------------------------------------------------------
CRect CMultiFrameBitmap::calcFrameRect (uint32_t frameIndex) const
{
if (getNumFrames () == 0)
return CRect ({}, getSize ());
if (frameIndex >= getNumFrames ())
frameIndex = getNumFrames () - 1;
auto rowIndex = frameIndex / getNumFramesPerRow ();
auto colIndex = frameIndex - (rowIndex * getNumFramesPerRow ());
CRect r;
r.left = getFrameSize ().x * colIndex;
r.right = r.left + getFrameSize ().x;
r.top = getFrameSize ().y * rowIndex;
r.bottom = r.top + getFrameSize ().y;
return r;
}
//-----------------------------------------------------------------------------
void CMultiFrameBitmap::drawFrame (CDrawContext* context, uint16_t frameIndex, CPoint pos)
{
auto fr = calcFrameRect (frameIndex);
auto r = CRect (pos, getFrameSize ());
draw (context, r, fr.getTopLeft ());
}
//-----------------------------------------------------------------------------
uint16_t CMultiFrameBitmap::normalizedValueToFrameIndex (float value) const
{
return normalizedToSteps<float, uint16_t> (value, getNumFrames () - 1);
}
//-----------------------------------------------------------------------------
float CMultiFrameBitmap::frameIndexToNormalizedValue (uint16_t frameIndex) const
{
return stepsToNormalized<float, uint16_t> (frameIndex, getNumFrames () - 1);
}
//-----------------------------------------------------------------------------
// CNinePartTiledBitmap Implementation
//-----------------------------------------------------------------------------
/*! @class CNinePartTiledBitmap
A nine-part tiled bitmap is tiled in nine parts which are drawing according to its part offsets:
- top left corner
- top right corner
- bottom left corner
- bottom right corner
- top edge, repeated as often as necessary and clipped appropriately
- left edge, dto.
- right edge, dto.
- bottom edge, dto.
- center, repeated horizontally and vertically as often as necessary
@verbatim
|------------------------------------------------------------------------------------------------|
| Top-Left Corner | <---- Top Edge ----> | Top-Right Corner |
|--------------------|-----------------------------------------------------|---------------------|
| ^ | ^ | ^ |
| | | | | | |
| Left Edge | <---- Center ----> | Right Edge |
| | | | | | |
| v | v | v |
|--------------------|-----------------------------------------------------|---------------------|
| Bottom-Left Corner | <---- Bottom Edge ----> | Bottom-Right Corner |
|------------------------------------------------------------------------------------------------|
@endverbatim
*/
//-----------------------------------------------------------------------------
CNinePartTiledBitmap::CNinePartTiledBitmap (const CResourceDescription& desc, const CNinePartTiledDescription& offsets)
: CBitmap (desc)
, offsets (offsets)
{
}
//-----------------------------------------------------------------------------
CNinePartTiledBitmap::CNinePartTiledBitmap (const PlatformBitmapPtr& platformBitmap, const CNinePartTiledDescription& offsets)
: CBitmap (platformBitmap)
, offsets (offsets)
{
}
//-----------------------------------------------------------------------------
void CNinePartTiledBitmap::draw (CDrawContext* inContext, const CRect& inDestRect, const CPoint& offset, float inAlpha)
{
inContext->drawBitmapNinePartTiled (this, inDestRect, offsets, inAlpha);
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
CBitmapPixelAccess::CBitmapPixelAccess ()
: bitmap (nullptr)
, pixelAccess (nullptr)
, currentPos (nullptr)
, address (nullptr)
, bytesPerRow (0)
, maxX (0)
, maxY (0)
, x (0)
, y (0)
{
}
//------------------------------------------------------------------------
void CBitmapPixelAccess::init (CBitmap* _bitmap, IPlatformBitmapPixelAccess* _pixelAccess)
{
bitmap = _bitmap;
pixelAccess = _pixelAccess;
address = currentPos = pixelAccess->getAddress ();
bytesPerRow = pixelAccess->getBytesPerRow ();
auto size = bitmap->getPlatformBitmap ()->getSize ();
maxX = static_cast<uint32_t> (size.x) - 1;
maxY = static_cast<uint32_t> (size.y) - 1;
}
/// @cond ignore
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
template <int32_t redPosition, int32_t greenPosition, int32_t bluePosition, int32_t alphaPosition>
class CBitmapPixelAccessOrder : public CBitmapPixelAccess
{
public:
void getColor (CColor& c) const override
{
c.red = currentPos[redPosition];
c.green = currentPos[greenPosition];
c.blue = currentPos[bluePosition];
c.alpha = currentPos[alphaPosition];
}
void setColor (const CColor& c) override
{
currentPos[redPosition] = c.red;
currentPos[greenPosition] = c.green;
currentPos[bluePosition] = c.blue;
currentPos[alphaPosition] = c.alpha;
}
};
/// @endcond
//------------------------------------------------------------------------
CBitmapPixelAccess* CBitmapPixelAccess::create (CBitmap* bitmap, bool alphaPremultiplied)
{
if (bitmap == nullptr || bitmap->getPlatformBitmap () == nullptr)
return nullptr;
auto pixelAccess = bitmap->getPlatformBitmap ()->lockPixels (alphaPremultiplied);
if (pixelAccess == nullptr)
return nullptr;
CBitmapPixelAccess* result = nullptr;
switch (pixelAccess->getPixelFormat ())
{
case IPlatformBitmapPixelAccess::kARGB: result = new CBitmapPixelAccessOrder<1,2,3,0> (); break;
case IPlatformBitmapPixelAccess::kRGBA: result = new CBitmapPixelAccessOrder<0,1,2,3> (); break;
case IPlatformBitmapPixelAccess::kABGR: result = new CBitmapPixelAccessOrder<3,2,1,0> (); break;
case IPlatformBitmapPixelAccess::kBGRA: result = new CBitmapPixelAccessOrder<2,1,0,3> (); break;
}
if (result)
result->init (bitmap, pixelAccess);
return result;
}
} // VSTGUI
+443
View File
@@ -0,0 +1,443 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "cpoint.h"
#include "crect.h"
#include "cresourcedescription.h"
#include "pixelbuffer.h"
#include "platform/iplatformbitmap.h"
#include <vector>
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CBitmap Declaration
//! @brief Encapsulates various platform depended kinds of bitmaps
//-----------------------------------------------------------------------------
class CBitmap : public AtomicReferenceCounted
{
public:
using BitmapVector = std::vector<PlatformBitmapPtr>;
using const_iterator = BitmapVector::const_iterator;
/** Create an image from a resource identifier */
explicit CBitmap (const CResourceDescription& desc);
/** Create an image with a given size */
CBitmap (CCoord width, CCoord height);
/** Create an image with a given size and scale factor */
CBitmap (CPoint size, double scaleFactor = 1.);
explicit CBitmap (const PlatformBitmapPtr& platformBitmap);
~CBitmap () noexcept override = default;
//-----------------------------------------------------------------------------
/// @name CBitmap Methods
//-----------------------------------------------------------------------------
//@{
virtual void draw (CDrawContext* context, const CRect& rect, const CPoint& offset = CPoint (0, 0), float alpha = 1.f);
/** get the width of the image */
CCoord getWidth () const;
/** get the height of the image */
CCoord getHeight () const;
/** get size of image */
CPoint getSize () const;
/** check if image is loaded */
bool isLoaded () const { return getPlatformBitmap () ? true : false; }
const CResourceDescription& getResourceDescription () const { return resourceDesc; }
PlatformBitmapPtr getPlatformBitmap () const;
void setPlatformBitmap (const PlatformBitmapPtr& bitmap);
bool addBitmap (const PlatformBitmapPtr& platformBitmap);
PlatformBitmapPtr getBestPlatformBitmapForScaleFactor (double scaleFactor) const;
const_iterator begin () const { return bitmaps.begin (); }
const_iterator end () const { return bitmaps.end (); }
//@}
//-----------------------------------------------------------------------------
protected:
CBitmap ();
CResourceDescription resourceDesc;
BitmapVector bitmaps;
};
//-----------------------------------------------------------------------------
/** Description for a multi frame bitmap
*
* @ingroup new_in_4_12
*/
struct CMultiFrameBitmapDescription
{
/** size of one frame */
CPoint frameSize {};
/** number of total frames */
uint16_t numFrames {};
/** number of frames per row */
uint16_t framesPerRow {1};
};
//-----------------------------------------------------------------------------
/** Multi frame bitmap
*
* A bitmap describing multiple frames ordered in rows and columns
*
* The index order is columns and then rows:
*
* 1.Row: 1 -> 2 -> 3
* 2.Row: 4 -> 5 -> 6
* ...
*
* @ingroup new_in_4_12
*/
class CMultiFrameBitmap : public CBitmap
{
public:
using CBitmap::CBitmap;
CMultiFrameBitmap (const CResourceDescription& desc,
CMultiFrameBitmapDescription multiFrameDesc);
/** set the multi frame description
*
* @param desc the multi frame description
* @return true if bitmap is big enough for the description
*/
bool setMultiFrameDesc (CMultiFrameBitmapDescription desc);
/** get the mult frame description */
CMultiFrameBitmapDescription getMultiFrameDesc () const;
/** get the frame size */
CPoint getFrameSize () const;
/** get the number of frames */
uint16_t getNumFrames () const;
/** get the number of frames per row */
uint16_t getNumFramesPerRow () const;
/** calculate the rect for one frame */
CRect calcFrameRect (uint32_t frameIndex) const;
/** draw one frame at the position in the context */
void drawFrame (CDrawContext* context, uint16_t frameIndex, CPoint pos);
/** return the frame to display for a normalized value
*
* defaults to:
* normalizedToSteps<float, uint16_t> (value, getNumFrames () - 1);
*
* subclasses can adopt this to other value mappings
*
* @ingroup new_in_4_12_1
*/
virtual uint16_t normalizedValueToFrameIndex (float value) const;
/** return the normalized value from the frame index
*
* defaults to:
* stepsToNormalized<float, uint16_t> (frameIndex, getNumFrames () - 1);
*
* subclasses can adopt this to other value mappings
*
* @ingroup new_in_4_12_1
*/
virtual float frameIndexToNormalizedValue (uint16_t frameIndex) const;
private:
CMultiFrameBitmapDescription description;
};
//------------------------------------------------------------------------
/** an injection class for views that draw frames of a CMultiFrameBitmap
*
* a view/control can inherit from this class to support drawing only frames in a range of the
* multi-frame bitmap.
*
* @ingroup new_in_4_12_2
*/
template<typename T>
class MultiFrameBitmapView
{
using This = T;
public:
/** set the range of the CMultiBitmapFrame this view will use for drawing
*
* @param startIndex the first frame to draw
* @param endIndex the last frame to draw
*/
void setMultiFrameBitmapRange (int32_t startIndex, int32_t endIndex)
{
if (endIndex >= 0 && startIndex > endIndex)
std::swap (startIndex, endIndex);
frameStartIndex = startIndex;
frameEndIndex = endIndex;
static_cast<This*> (this)->invalid ();
}
/** get the range of the CMulitBitmapFrame this view will use for drawing
*
* @return a std::pair with the start and end index
*/
std::pair<int32_t, int32_t> getMultiFrameBitmapRange () const
{
return {frameStartIndex, frameEndIndex};
}
/** get the number of frames this view will use for drawing
*
* @param mfb the bitmap
* @return the number of frames
*/
uint16_t getMultiFrameBitmapRangeLength (const CMultiFrameBitmap& mfb) const
{
auto endIndex = frameEndIndex >= 0 ? frameEndIndex : mfb.getNumFrames ();
return endIndex - frameStartIndex;
}
/** get the inverse index
*
* @param mfb the bitmap
* @param index the index
* @return the inverse index
*/
uint16_t getInverseIndex (const CMultiFrameBitmap& mfb, uint16_t index) const
{
auto endIndex = frameEndIndex >= 0 ? frameEndIndex : mfb.getNumFrames () - 1;
if (index >= frameStartIndex && index <= endIndex)
{
return endIndex - (index - frameStartIndex);
}
return index;
}
/** get the frame index for a normalized value
*
* @param mfb the bitmap
* @param normValue the normalized value
* @return the index of the frame for the value
*/
uint16_t getMultiFrameBitmapIndex (const CMultiFrameBitmap& mfb, float normValue) const
{
if (frameStartIndex == 0 && frameEndIndex < 0)
return mfb.normalizedValueToFrameIndex (normValue);
auto startNorm = mfb.frameIndexToNormalizedValue (frameStartIndex);
auto endNorm = mfb.frameIndexToNormalizedValue (
frameEndIndex >= 0 ? frameEndIndex : mfb.getNumFrames () - 1);
normValue = normValue * (endNorm - startNorm) + startNorm;
return mfb.normalizedValueToFrameIndex (normValue);
}
/** get the normalized value for a frame index
*
* @param mfb the bitmap
* @param index the frame index
* @return the normalized value
*/
float getNormValueFromMultiFrameBitmapIndex (const CMultiFrameBitmap& mfb, uint16_t index) const
{
auto startNorm = mfb.frameIndexToNormalizedValue (frameStartIndex);
auto endNorm = mfb.frameIndexToNormalizedValue (
frameEndIndex >= 0 ? frameEndIndex : mfb.getNumFrames () - 1);
auto indexNorm = mfb.frameIndexToNormalizedValue (index);
return (indexNorm - startNorm) / (endNorm - startNorm);
}
private:
int32_t frameStartIndex {0};
int32_t frameEndIndex {-1};
};
//-----------------------------------------------------------------------------
struct CNinePartTiledDescription
{
enum
{
kPartTopLeft,
kPartTop,
kPartTopRight,
kPartLeft,
kPartCenter,
kPartRight,
kPartBottomLeft,
kPartBottom,
kPartBottomRight,
kPartCount
};
CCoord left {0.};
CCoord top {0.};
CCoord right {0.};
CCoord bottom {0.};
CNinePartTiledDescription () = default;
CNinePartTiledDescription (CCoord left, CCoord top, CCoord right, CCoord bottom)
: left (left), top (top), right (right), bottom (bottom) {}
//-----------------------------------------------------------------------------
inline void calcRects (const CRect& inBitmapRect, CRect outRect[kPartCount]) const
{
// Center
CRect myCenter = outRect[kPartCenter] (inBitmapRect.left + left,
inBitmapRect.top + top,
inBitmapRect.right - right,
inBitmapRect.bottom - bottom);
// Edges
outRect[kPartTop] (myCenter.left, inBitmapRect.top, myCenter.right, myCenter.top);
outRect[kPartLeft] (inBitmapRect.left, myCenter.top, myCenter.left, myCenter.bottom);
outRect[kPartRight] (myCenter.right, myCenter.top, inBitmapRect.right, myCenter.bottom);
outRect[kPartBottom] (myCenter.left, myCenter.bottom, myCenter.right, inBitmapRect.bottom);
// Corners
outRect[kPartTopLeft] (inBitmapRect.left, inBitmapRect.top, myCenter.left, myCenter.top);
outRect[kPartTopRight] (myCenter.right, inBitmapRect.top, inBitmapRect.right, myCenter.top);
outRect[kPartBottomLeft] (inBitmapRect.left, myCenter.bottom, myCenter.left, inBitmapRect.bottom);
outRect[kPartBottomRight] (myCenter.right, myCenter.bottom, inBitmapRect.right, inBitmapRect.bottom);
}
};
//-----------------------------------------------------------------------------
// CNinePartTiledBitmap Declaration
/// @brief a nine-part tiled bitmap
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class CNinePartTiledBitmap : public CBitmap
{
public:
CNinePartTiledBitmap (const CResourceDescription& desc, const CNinePartTiledDescription& offsets);
CNinePartTiledBitmap (const PlatformBitmapPtr& platformBitmap, const CNinePartTiledDescription& offsets);
~CNinePartTiledBitmap () noexcept override = default;
//-----------------------------------------------------------------------------
/// @name Part Offsets
//-----------------------------------------------------------------------------
//@{
void setPartOffsets (const CNinePartTiledDescription& partOffsets) { offsets = partOffsets; }
const CNinePartTiledDescription& getPartOffsets () const { return offsets; }
//@}
void draw (CDrawContext* context, const CRect& rect, const CPoint& offset = CPoint (0, 0), float alpha = 1.f) override;
//-----------------------------------------------------------------------------
protected:
CNinePartTiledDescription offsets;
};
//------------------------------------------------------------------------
/** Convert between Platform Pixel Accessor pixel format and PixelBuffer format */
template<typename T1, typename T2,
typename std::enable_if<
std::is_same<PixelBuffer::Format, T2>::value ||
std::is_same<IPlatformBitmapPixelAccess::PixelFormat, T2>::value>::type* = nullptr>
inline T1 convert (T2 format)
{
using PlPixelFormat = IPlatformBitmapPixelAccess::PixelFormat;
using Format = PixelBuffer::Format;
static_assert (std::is_same<Format, T1>::value || std::is_same<PlPixelFormat, T1>::value,
"Unexpected Format");
static_assert (!std::is_same<T1, T2>::value, "Unexpected Format");
static_assert (static_cast<int32_t> (Format::ARGB) == PlPixelFormat::kARGB, "Format Mismatch");
static_assert (static_cast<int32_t> (Format::ABGR) == PlPixelFormat::kABGR, "Format Mismatch");
static_assert (static_cast<int32_t> (Format::RGBA) == PlPixelFormat::kRGBA, "Format Mismatch");
static_assert (static_cast<int32_t> (Format::BGRA) == PlPixelFormat::kBGRA, "Format Mismatch");
return static_cast<T1> (format);
}
//------------------------------------------------------------------------
// CBitmapPixelAccess
/// @brief direct pixel access to a CBitmap
/// @ingroup new_in_4_0
//------------------------------------------------------------------------
class CBitmapPixelAccess : public AtomicReferenceCounted
{
public:
/** advance position */
inline bool operator++ ();
/** set current position */
inline bool setPosition (uint32_t x, uint32_t y);
/** return current x position */
inline uint32_t getX () const { return x; }
/** return current y position */
inline uint32_t getY () const { return y; }
/** get color of current pixel */
virtual void getColor (CColor& c) const = 0;
/** set color of current pixel */
virtual void setColor (const CColor& c) = 0;
/** get native color value */
inline void getValue (uint32_t& value);
/** set native color value */
inline void setValue (uint32_t value);
inline uint32_t getBitmapWidth () const { return maxX+1; }
inline uint32_t getBitmapHeight () const { return maxY+1; }
inline IPlatformBitmapPixelAccess* getPlatformBitmapPixelAccess () const { return pixelAccess; }
/** create an accessor.
can return 0 if platform implementation does not support this.
result needs to be forgotten before the CBitmap reflects the change to the pixels */
static CBitmapPixelAccess* create (CBitmap* bitmap, bool alphaPremultiplied = true);
//-----------------------------------------------------------------------------
protected:
CBitmapPixelAccess ();
~CBitmapPixelAccess () noexcept override = default;
void init (CBitmap* bitmap, IPlatformBitmapPixelAccess* pixelAccess);
CBitmap* bitmap;
SharedPointer<IPlatformBitmapPixelAccess> pixelAccess;
uint8_t* currentPos;
uint8_t* address;
uint32_t bytesPerRow;
uint32_t maxX;
uint32_t maxY;
uint32_t x;
uint32_t y;
};
//------------------------------------------------------------------------
inline bool CBitmapPixelAccess::operator++ ()
{
if (x < maxX)
{
x++;
currentPos += 4;
return true;
}
else if (y < maxY)
{
y++;
x = 0;
currentPos = address + y * bytesPerRow;
return true;
}
return false;
}
//------------------------------------------------------------------------
inline bool CBitmapPixelAccess::setPosition (uint32_t _x, uint32_t _y)
{
if (_x > maxX || _y > maxY)
return false;
x = _x;
y = _y;
currentPos = address + y * bytesPerRow + x * 4;
return true;
}
//------------------------------------------------------------------------
inline void CBitmapPixelAccess::getValue (uint32_t& value)
{
value = *(uint32_t*) (currentPos);
}
//------------------------------------------------------------------------
inline void CBitmapPixelAccess::setValue (uint32_t value)
{
*(uint32_t*) (currentPos) = value;
}
} // VSTGUI
File diff suppressed because it is too large Load Diff
+236
View File
@@ -0,0 +1,236 @@
// 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
#pragma once
#include "vstguifwd.h"
#include <vector>
#include <string>
#include <map>
namespace VSTGUI {
namespace BitmapFilter {
//----------------------------------------------------------------------------------------------------
/// @brief Filter Property
/// @ingroup new_in_4_1
//----------------------------------------------------------------------------------------------------
class Property
{
public:
enum Type {
kUnknown = 0,
kInteger,
kFloat,
kObject,
kRect,
kPoint,
kColor,
kTransformMatrix
};
Property (Type type = kUnknown);
Property (int32_t intValue);
Property (double floatValue);
Property (IReference* objectValue);
Property (const CRect& rectValue);
Property (const CPoint& pointValue);
Property (const CColor& colorValue);
Property (const CGraphicsTransform& transformValue);
Property (const Property& p);
Property (Property&& p) noexcept;
~Property () noexcept;
Type getType () const { return type; }
int32_t getInteger () const;
double getFloat () const;
IReference* getObject () const;
const CRect& getRect () const;
const CPoint& getPoint () const;
const CColor& getColor () const;
const CGraphicsTransform& getTransform () const;
Property& operator=(const Property& p);
Property& operator=(Property&& p) noexcept;
//----------------------------------------------------------------------------------------------------
private:
template<typename T> void assign (T value);
Type type;
void* value;
};
//----------------------------------------------------------------------------------------------------
/// @brief Filter Interface
/// @ingroup new_in_4_1
//----------------------------------------------------------------------------------------------------
class IFilter : public NonAtomicReferenceCounted
{
public:
virtual bool run (bool replaceInputBitmap = false) = 0;
virtual UTF8StringPtr getDescription () const = 0;
virtual bool setProperty (IdStringPtr name, const Property& property) = 0;
virtual bool setProperty (IdStringPtr name, Property&& property) = 0;
virtual const Property& getProperty (IdStringPtr name) const = 0;
virtual uint32_t getNumProperties () const = 0;
virtual IdStringPtr getPropertyName (uint32_t index) const = 0;
virtual Property::Type getPropertyType (uint32_t index) const = 0;
virtual Property::Type getPropertyType (IdStringPtr name) const = 0;
using CreateFunction = IFilter* (*) (IdStringPtr name);
};
//----------------------------------------------------------------------------------------------------
/// @brief Bitmap Filter Factory.
/// @ingroup new_in_4_1
/// @details See @ref VSTGUI::BitmapFilter::Standard for a description of included Filters
//----------------------------------------------------------------------------------------------------
class Factory
{
public:
static Factory& getInstance ();
uint32_t getNumFilters () const;
IdStringPtr getFilterName (uint32_t index) const;
IFilter* createFilter (IdStringPtr name) const;
bool registerFilter (IdStringPtr name, IFilter::CreateFunction createFunction);
bool unregisterFilter (IdStringPtr name, IFilter::CreateFunction createFunction);
protected:
using FilterMap = std::map<std::string, IFilter::CreateFunction>;
FilterMap filters;
};
/** @brief Standard Bitmap Filter Names */
namespace Standard {
/** Box Blur Filter Name.
Applies a box blur on the input bitmap.
Properties:
- Property::kInputBitmap
- Property::kRadius
- Property::kOutputBitmap
*/
static const IdStringPtr kBoxBlur = "Box Blur";
/** Grayscale Filter Name.
Produces a grayscale version of the input bitmap.
Properties:
- Property::kInputBitmap
- Property::kOutputBitmap
- Property::kAlphaChannelOnly
*/
static const IdStringPtr kGrayscale = "Grayscale";
/** Replace Color Filter Name.
Replaces the colors which match the input color to the output color.
Properties:
- Property::kInputBitmap
- Property::kInputColor
- Property::kOutputColor
- Property::kOutputBitmap
*/
static const IdStringPtr kReplaceColor = "Replace Color";
/** Set Color Filter Name.
Sets all colors of the input bitmap the color of the input color. If Property::kIgnoreAlphaColorValue is set, the
alpha value of the input bitmap is not changed.
Properties:
- Property::kInputBitmap
- Property::kInputColor
- Property::kIgnoreAlphaColorValue
- Property::kOutputBitmap
*/
static const IdStringPtr kSetColor = "Set Color";
/** Scale Bilinear Filter Name.
Creates a bilinear scaled bitmap of the input bitmap.
Does not work inplace.
Properties:
- Property::kInputBitmap
- Property::kOutputRect
- Property::kOutputBitmap
*/
static const IdStringPtr kScaleBilinear = "Scale Biliniear";
/** Scale Linear Filter Name.
Creates a linear scaled bitmap of the input bitmap.
Does not work inplace.
Properties:
- Property::kInputBitmap
- Property::kOutputRect
- Property::kOutputBitmap
*/
static const IdStringPtr kScaleLinear = "Scale Linear";
/** @brief Standard Bitmap Property Names */
namespace Property
{
/** [Property::kObject - CBitmap] */
static const IdStringPtr kInputBitmap = "InputBitmap";
/** [Property::kObject - CBitmap] */
static const IdStringPtr kOutputBitmap = "OutputBitmap";
/** [Property::kInteger] */
static const IdStringPtr kRadius = "Radius";
/** [Property::kColor] */
static const IdStringPtr kInputColor = "InputColor";
/** [Property::kColor] */
static const IdStringPtr kOutputColor = "OutputColor";
/** [Property::kRect] */
static const IdStringPtr kOutputRect = "OutputRect";
/** [Property::kInteger] */
static const IdStringPtr kIgnoreAlphaColorValue = "IgnoreAlphaColorValue";
/** [Property::kInteger] */
static const IdStringPtr kAlphaChannelOnly = "AlphaChannelOnly";
} // Property
} // Standard
//----------------------------------------------------------------------------------------------------
/// @brief A Base Class for Implementing Bitmap Filters
/// @ingroup new_in_4_1
//----------------------------------------------------------------------------------------------------
class FilterBase : public IFilter
{
protected:
FilterBase (UTF8StringPtr description);
bool registerProperty (IdStringPtr name, const Property& defaultProperty);
CBitmap* getInputBitmap () const;
UTF8StringPtr getDescription () const override;
bool setProperty (IdStringPtr name, const Property& property) override;
bool setProperty (IdStringPtr name, Property&& property) override;
const Property& getProperty (IdStringPtr name) const override;
uint32_t getNumProperties () const override;
IdStringPtr getPropertyName (uint32_t index) const override;
Property::Type getPropertyType (uint32_t index) const override;
Property::Type getPropertyType (IdStringPtr name) const override;
private:
using PropertyMap = std::map<std::string, Property>;
std::string description;
PropertyMap properties;
};
} // BitmapFilter
} // VSTGUI
+95
View File
@@ -0,0 +1,95 @@
// 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
#pragma once
#include "vstguibase.h"
namespace VSTGUI {
//----------------------------
// @brief Button Types (+modifiers)
//----------------------------
enum CButton
{
/** left mouse button */
kLButton = 1 << 1,
/** middle mouse button */
kMButton = 1 << 2,
/** right mouse button */
kRButton = 1 << 3,
/** shift modifier */
kShift = 1 << 4,
/** control modifier (Command Key on Mac OS X and Control Key on Windows) */
kControl = 1 << 5,
/** alt modifier */
kAlt = 1 << 6,
/** apple modifier (Mac OS X only. Is the Control key) */
kApple = 1 << 7,
/** 4th mouse button */
kButton4 = 1 << 8,
/** 5th mouse button */
kButton5 = 1 << 9,
/** mouse button is double click */
kDoubleClick = 1 << 10,
/** system mouse wheel setting is inverted (Only valid for onMouseWheel methods). The distance
value is already transformed back to non inverted. But for scroll views we need to know if we
need to invert it back. */
kMouseWheelInverted = 1 << 11
};
//-----------------------------------------------------------------------------
// CButtonState Declaration
//! @brief Button and Modifier state
//-----------------------------------------------------------------------------
struct CButtonState
{
public:
CButtonState (int32_t state = 0) : state (state) {}
CButtonState (const CButtonState& bs) : state (bs.state) {}
int32_t getButtonState () const { return state & (kLButton | kRButton | kMButton | kButton4 | kButton5); }
int32_t getModifierState () const { return state & (kShift | kAlt | kControl | kApple); }
/** returns true if only the left button is set. Ignores modifier state */
bool isLeftButton () const { return getButtonState () == kLButton; }
/** returns true if only the middle button is set. Ignores modifier state */
bool isMiddleButton () const { return getButtonState () == kMButton; }
/** returns true if only the right button is set. Ignores modifier state */
bool isRightButton () const { return getButtonState () == kRButton; }
/** returns true if only the 4th button is set. Ignores modifier state */
bool isButton4 () const { return getButtonState () == kButton4; }
/** returns true if only the 5th button is set. Ignores modifier state */
bool isButton5 () const { return getButtonState () == kButton5; }
/** returns true if the double click flag is set. */
bool isDoubleClick () const { return hasBit<int32_t> (state, kDoubleClick); }
/** returns true if the shift modifier is set. */
bool isShiftSet () const { return hasBit<int32_t> (state, kShift); }
/** returns true if the alt modifier is set. */
bool isAltSet () const { return hasBit<int32_t> (state, kAlt); }
/** returns true if the control modifier is set. */
bool isControlSet () const { return hasBit<int32_t> (state, kControl); }
/** returns true if the apple modifier is set. */
bool isAppleSet () const { return hasBit<int32_t> (state, kApple); }
bool isMouseWheelInverted () const { return hasBit<int32_t> (state, kMouseWheelInverted); }
int32_t operator() () const { return state; }
CButtonState& operator= (int32_t s) { state = s; return *this; }
CButtonState& operator&= (int32_t s) { state &= s; return *this; }
CButtonState& operator|= (int32_t s) { state |= s; return *this; }
int32_t operator& (const CButtonState& s) const { return state & s.state; }
int32_t operator| (const CButtonState& s) const { return state | s.state; }
int32_t operator~ () const { return ~state; }
bool operator== (const CButtonState& s) const { return state == s.state; }
bool operator!= (const CButtonState& s) const { return state != s.state; }
protected:
int32_t state;
};
} // VSTGUI
+104
View File
@@ -0,0 +1,104 @@
// 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 "cclipboard.h"
#include "platform/platformfactory.h"
#include <string_view>
namespace VSTGUI {
namespace CClipboardDetail {
//-----------------------------------------------------------------------------
template<bool AsFile>
struct StringDataPackage : IDataPackage
{
StringDataPackage (std::string_view str) : str (str) {}
uint32_t getCount () const final { return 1; }
uint32_t getDataSize (uint32_t index) const final
{
return static_cast<uint32_t> (str.size ());
}
Type getDataType (uint32_t index) const final { return AsFile ? Type::kFilePath : Type::kText; }
uint32_t getData (uint32_t index, const void*& buffer, Type& type) const final
{
buffer = str.data ();
type = getDataType (index);
return getDataSize (index);
}
std::string str;
};
//-----------------------------------------------------------------------------
template<bool AsFile>
Optional<UTF8String> getString (IDataPackage* cb)
{
for (auto i = 0u, count = cb->getCount (); i < count; i++)
{
if (cb->getDataType (i) == (AsFile ? IDataPackage::Type::kFilePath
: IDataPackage::Type::kText))
{
IDataPackage::Type type;
const void* data = nullptr;
auto size = cb->getData (i, data, type);
if (size > 0)
{
return {UTF8String (std::string (static_cast<const char*> (data), size))};
}
}
}
return {};
}
} // CClipboardDetail
//-----------------------------------------------------------------------------
SharedPointer<IDataPackage> CClipboard::get ()
{
return getPlatformFactory ().getClipboard ();
}
//-----------------------------------------------------------------------------
bool CClipboard::set (const SharedPointer<IDataPackage>& data)
{
return getPlatformFactory ().setClipboard (data);
}
//-----------------------------------------------------------------------------
bool CClipboard::setString (UTF8StringPtr str)
{
return set (makeOwned<CClipboardDetail::StringDataPackage<false>> (
std::string_view (str, strlen (str))));
}
//-----------------------------------------------------------------------------
bool CClipboard::setFilePath (UTF8StringPtr str)
{
return set (makeOwned<CClipboardDetail::StringDataPackage<true>> (
std::string_view (str, strlen (str))));
}
//-----------------------------------------------------------------------------
Optional<UTF8String> CClipboard::getString ()
{
if (auto cb = get ())
{
return CClipboardDetail::getString<false> (cb);
}
return {};
}
//-----------------------------------------------------------------------------
Optional<UTF8String> CClipboard::getFilePath ()
{
if (auto cb = get ())
{
return CClipboardDetail::getString<true> (cb);
}
return {};
}
} // VSTGUI
+32
View File
@@ -0,0 +1,32 @@
// 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
#pragma once
#include "cstring.h"
#include "idatapackage.h"
#include "optional.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
struct CClipboard
{
/** get the global clipboard data */
static SharedPointer<IDataPackage> get ();
/** set the global clipboard data */
static bool set (const SharedPointer<IDataPackage>& data);
/** get the string from the global clipboard if it exists */
static Optional<UTF8String> getString ();
/** get the file path from the global clipboard if it exists */
static Optional<UTF8String> getFilePath ();
/** set the string of the global clipboard */
static bool setString (UTF8StringPtr str);
/** set the file path of the global clipboard */
static bool setFilePath (UTF8StringPtr str);
};
} // VSTGUI
+487
View File
@@ -0,0 +1,487 @@
// 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 "ccolor.h"
#include "cstring.h"
#include "algorithm.h"
#include <cmath>
#include <sstream>
#include <iomanip>
namespace VSTGUI {
/// @cond ignore
//-----------------------------------------------------------------------------
template<typename _Tp>
inline const _Tp& min3 (const _Tp& v1, const _Tp& v2, const _Tp& v3)
{
if (v1 <= v2)
return (v1 <= v3) ? v1 : v3;
return (v2 <= v3) ? v2 : v3;
}
//-----------------------------------------------------------------------------
template<typename _Tp>
inline const _Tp& max3 (const _Tp& v1, const _Tp& v2, const _Tp& v3)
{
if (v1 >= v2)
return (v1 >= v3) ? v1 : v3;
return (v2 >= v3) ? v2 : v3;
}
/// @endcond
//-----------------------------------------------------------------------------
uint8_t CColor::getLightness () const
{
return (max3<uint8_t> (red, green, blue) / 2) + (min3<uint8_t>(red, green, blue) / 2);
}
//-----------------------------------------------------------------------------
void CColor::toHSL (double& hue, double& saturation, double& lightness) const
{
double r = normRed<double> ();
double g = normGreen<double> ();
double b = normBlue<double> ();
double M = max3<double> (r, g ,b);
double m = min3<double> (r, g ,b);
double C = M - m;
lightness = (M + m) / 2.;
if (C == 0.)
{
hue = saturation = 0.;
return;
}
if (M == r)
{
hue = fmod (((g-b) / C), 6.);
}
else if (M == g)
{
hue = ((b - r) / C) + 2.;
}
else if (M == b)
{
hue = ((r - g) / C) + 4.;
}
hue *= 60.;
if (hue < 0.0)
hue += 360.0;
if (lightness <= 0.5)
saturation = C / (2. * lightness);
else
saturation = C / (2. - 2. * lightness);
}
//-----------------------------------------------------------------------------
void CColor::fromHSL (double hue, double saturation, double lightness)
{
while (hue > 360.)
hue -= 360.;
while (hue < 0.)
hue += 360.;
double C = (1. - fabs (2 * lightness - 1)) * saturation;
double H = hue / 60.;
double X = C * (1. - fabs (fmod (H, 2) - 1.));
double r,g,b;
if (H >= 0 && H < 1.)
{
r = C;
g = X;
b = 0.;
}
else if (H >= 1. && H < 2.)
{
r = X;
g = C;
b = 0.;
}
else if (H >= 2. && H < 3.)
{
r = 0.;
g = C;
b = X;
}
else if (H >= 3. && H < 4.)
{
r = 0.;
g = X;
b = C;
}
else if (H >= 4. && H < 5.)
{
r = X;
g = 0.;
b = C;
}
else // if (H >= 5. && H <= 6.)
{
r = C;
g = 0.;
b = X;
}
double m = lightness - (C / 2.);
setNormRed (clampNorm (r + m));
setNormGreen (clampNorm (g + m));
setNormBlue (clampNorm (b + m));
}
//-----------------------------------------------------------------------------
void CColor::toHSV (double& hue, double& saturation, double& value) const
{
double rgbMax = (max3<uint8_t> (red, green, blue)) / 255.;
value = rgbMax;
if (value == 0)
{
hue = saturation = 0;
return;
}
/* Normalize value to 1 */
double r = normRed<double> () / value;
double g = normGreen<double> () / value;
double b = normBlue<double> () / value;
double rgbMin = min3<double> (r, g, b);
rgbMax = max3<double> (r, g, b);
saturation = rgbMax - rgbMin;
if (saturation == 0)
{
hue = 0.;
return;
}
/* Normalize saturation to 1 */
r = (r - rgbMin)/(rgbMax - rgbMin);
g = (g - rgbMin)/(rgbMax - rgbMin);
b = (b - rgbMin)/(rgbMax - rgbMin);
rgbMax = max3<double> (r, g, b);
/* Compute hue */
if (rgbMax == r)
{
hue = 0.0 + 60.0 * (g - b);
}
else if (rgbMax == g)
{
hue = 120.0 + 60.0 * (b - r);
}
else /* rgbMax == b */
{
hue = 240.0 + 60.0 * (r - g);
}
if (hue < 0.0)
{
hue += 360.0;
}
}
//-----------------------------------------------------------------------------
void CColor::fromHSV (double hue, double saturation, double value)
{
if (value <= 0.)
{
red = green = blue = 0;
return;
}
else if (value > 1.)
value = 1.;
if (saturation <= 0.)
{
red = green = blue = static_cast<uint8_t> (value * 255.);
return;
}
else if (saturation > 1.)
saturation = 1.;
while (hue > 360.)
hue -= 360.;
while (hue < 0.)
hue += 360.;
const double hf = hue / 60.0;
const int32_t i = static_cast<int32_t> (floor (hf));
const double f = hf - i;
const double pv = value * ( 1 - saturation );
const double qv = value * ( 1 - saturation * f );
const double tv = value * ( 1 - saturation * ( 1 - f ) );
double r = 0.;
double g = 0.;
double b = 0.;
switch (i)
{
// red is dominant
case 0:
{
r = value;
g = tv;
b = pv;
break;
}
case 5:
{
r = value;
g = pv;
b = qv;
break;
}
case 6:
{
r = value;
g = tv;
b = pv;
break;
}
case -1:
{
r = value;
g = pv;
b = qv;
break;
}
// green is dominant
case 1:
{
r = qv;
g = value;
b = pv;
break;
}
case 2:
{
r = pv;
g = value;
b = tv;
break;
}
// blue is dominant
case 3:
{
r = pv;
g = qv;
b = value;
break;
}
case 4:
{
r = tv;
g = pv;
b = value;
break;
}
}
setNormRed (clampNorm (r));
setNormGreen (clampNorm (g));
setNormBlue (clampNorm (b));
}
//------------------------------------------------------------------------
bool CColor::fromString (std::string_view str)
{
if (!isColorRepresentation (str))
return false;
std::string rv (str.data () + 1, 2);
std::string gv (str.data () + 3, 2);
std::string bv (str.data () + 5, 2);
std::string av (str.data () + 7, 2);
red = (uint8_t)strtol (rv.data (), nullptr, 16);
green = (uint8_t)strtol (gv.data (), nullptr, 16);
blue = (uint8_t)strtol (bv.data (), nullptr, 16);
alpha = (uint8_t)strtol (av.data (), nullptr, 16);
return true;
}
//------------------------------------------------------------------------
bool CColor::isColorRepresentation (std::string_view str)
{
return (str.size () == 9 && str.data ()[0] == '#');
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//-----------------------------------------------------------------------------
bool CColor::isColorRepresentation (UTF8StringPtr str)
{
if (str && isColorRepresentation ({str, strlen (str)}))
return true;
return false;
}
//-----------------------------------------------------------------------------
bool CColor::fromString (UTF8StringPtr str)
{
if (!str)
return false;
return fromString ({str, strlen (str)});
}
#endif // VSTGUI_ENABLE_DEPRECATED_METHODS
//-----------------------------------------------------------------------------
UTF8String CColor::toString () const
{
std::stringstream str;
str << "#";
str << std::hex << std::setw (2) << std::setfill ('0') << static_cast<int32_t> (red);
str << std::hex << std::setw (2) << std::setfill ('0') << static_cast<int32_t> (green);
str << std::hex << std::setw (2) << std::setfill ('0') << static_cast<int32_t> (blue);
str << std::hex << std::setw (2) << std::setfill ('0') << static_cast<int32_t> (alpha);
return UTF8String (str.str ());
}
using namespace std::literals;
//-----------------------------------------------------------------------------
static constexpr std::array<CSSNamedColor, 148> namedColors = {
{{"transparent"sv, {0, 0, 0, 0}},
{"aliceblue"sv, {240, 248, 255, 255}},
{"antiquewhite"sv, {250, 235, 215, 255}},
{"aqua"sv, {0, 255, 255, 255}},
{"aquamarine"sv, {127, 255, 212, 255}},
{"azure"sv, {240, 255, 255, 255}},
{"beige"sv, {245, 245, 220, 255}},
{"bisque"sv, {255, 228, 196, 255}},
{"black"sv, {0, 0, 0, 255}},
{"blanchedalmond"sv, {255, 235, 205, 255}},
{"blue"sv, {0, 0, 255, 255}},
{"blueviolet"sv, {138, 43, 226, 255}},
{"brown"sv, {165, 42, 42, 255}},
{"burlywood"sv, {222, 184, 135, 255}},
{"cadetblue"sv, {95, 158, 160, 255}},
{"chartreuse"sv, {127, 255, 0, 255}},
{"chocolate"sv, {210, 105, 30, 255}},
{"coral"sv, {255, 127, 80, 255}},
{"cornflowerblue"sv, {100, 149, 237, 255}},
{"cornsilk"sv, {255, 248, 220, 255}},
{"crimson"sv, {220, 20, 60, 255}},
{"cyan"sv, {0, 255, 255, 255}},
{"darkblue"sv, {0, 0, 139, 255}},
{"darkcyan"sv, {0, 139, 139, 255}},
{"darkgoldenrod"sv, {184, 134, 11, 255}},
{"darkgray"sv, {169, 169, 169, 255}},
{"darkgreen"sv, {0, 100, 0, 255}},
{"darkgrey"sv, {169, 169, 169, 255}},
{"darkkhaki"sv, {189, 183, 107, 255}},
{"darkmagenta"sv, {139, 0, 139, 255}},
{"darkolivegreen"sv, {85, 107, 47, 255}},
{"darkorange"sv, {255, 140, 0, 255}},
{"darkorchid"sv, {153, 50, 204, 255}},
{"darkred"sv, {139, 0, 0, 255}},
{"darksalmon"sv, {233, 150, 122, 255}},
{"darkseagreen"sv, {143, 188, 143, 255}},
{"darkslateblue"sv, {72, 61, 139, 255}},
{"darkslategray"sv, {47, 79, 79, 255}},
{"darkslategrey"sv, {47, 79, 79, 255}},
{"darkturquoise"sv, {0, 206, 209, 255}},
{"darkviolet"sv, {148, 0, 211, 255}},
{"deeppink"sv, {255, 20, 147, 255}},
{"deepskyblue"sv, {0, 191, 255, 255}},
{"dimgray"sv, {105, 105, 105, 255}},
{"dimgrey"sv, {105, 105, 105, 255}},
{"dodgerblue"sv, {30, 144, 255, 255}},
{"firebrick"sv, {178, 34, 34, 255}},
{"floralwhite"sv, {255, 250, 240, 255}},
{"forestgreen"sv, {34, 139, 34, 255}},
{"fuchsia"sv, {255, 0, 255, 255}},
{"gainsboro"sv, {220, 220, 220, 255}},
{"ghostwhite"sv, {248, 248, 255, 255}},
{"gold"sv, {255, 215, 0, 255}},
{"goldenrod"sv, {218, 165, 32, 255}},
{"gray"sv, {128, 128, 128, 255}},
{"green"sv, {0, 128, 0, 255}},
{"greenyellow"sv, {173, 255, 47, 255}},
{"grey"sv, {128, 128, 128, 255}},
{"honeydew"sv, {240, 255, 240, 255}},
{"hotpink"sv, {255, 105, 180, 255}},
{"indianred"sv, {205, 92, 92, 255}},
{"indigo"sv, {75, 0, 130, 255}},
{"ivory"sv, {255, 255, 240, 255}},
{"khaki"sv, {240, 230, 140, 255}},
{"lavender"sv, {230, 230, 250, 255}},
{"lavenderblush"sv, {255, 240, 245, 255}},
{"lawngreen"sv, {124, 252, 0, 255}},
{"lemonchiffon"sv, {255, 250, 205, 255}},
{"lightblue"sv, {173, 216, 230, 255}},
{"lightcoral"sv, {240, 128, 128, 255}},
{"lightcyan"sv, {224, 255, 255, 255}},
{"lightgoldenrodyellow"sv, {250, 250, 210, 255}},
{"lightgray"sv, {211, 211, 211, 255}},
{"lightgreen"sv, {144, 238, 144, 255}},
{"lightgrey"sv, {211, 211, 211, 255}},
{"lightpink"sv, {255, 182, 193, 255}},
{"lightsalmon"sv, {255, 160, 122, 255}},
{"lightseagreen"sv, {32, 178, 170, 255}},
{"lightskyblue"sv, {135, 206, 250, 255}},
{"lightslategray"sv, {119, 136, 153, 255}},
{"lightslategrey"sv, {119, 136, 153, 255}},
{"lightsteelblue"sv, {176, 196, 222, 255}},
{"lightyellow"sv, {255, 255, 224, 255}},
{"lime"sv, {0, 255, 0, 255}},
{"limegreen"sv, {50, 205, 50, 255}},
{"linen"sv, {250, 240, 230, 255}},
{"magenta"sv, {255, 0, 255, 255}},
{"maroon"sv, {128, 0, 0, 255}},
{"mediumaquamarine"sv, {102, 205, 170, 255}},
{"mediumblue"sv, {0, 0, 205, 255}},
{"mediumorchid"sv, {186, 85, 211, 255}},
{"mediumpurple"sv, {147, 112, 219, 255}},
{"mediumseagreen"sv, {60, 179, 113, 255}},
{"mediumslateblue"sv, {123, 104, 238, 255}},
{"mediumspringgreen"sv, {0, 250, 154, 255}},
{"mediumturquoise"sv, {72, 209, 204, 255}},
{"mediumvioletred"sv, {199, 21, 133, 255}},
{"midnightblue"sv, {25, 25, 112, 255}},
{"mintcream"sv, {245, 255, 250, 255}},
{"mistyrose"sv, {255, 228, 225, 255}},
{"moccasin"sv, {255, 228, 181, 255}},
{"navajowhite"sv, {255, 222, 173, 255}},
{"navy"sv, {0, 0, 128, 255}},
{"oldlace"sv, {253, 245, 230, 255}},
{"olive"sv, {128, 128, 0, 255}},
{"olivedrab"sv, {107, 142, 35, 255}},
{"orange"sv, {255, 165, 0, 255}},
{"orangered"sv, {255, 69, 0, 255}},
{"orchid"sv, {218, 112, 214, 255}},
{"palegoldenrod"sv, {238, 232, 170, 255}},
{"palegreen"sv, {152, 251, 152, 255}},
{"paleturquoise"sv, {175, 238, 238, 255}},
{"palevioletred"sv, {219, 112, 147, 255}},
{"papayawhip"sv, {255, 239, 213, 255}},
{"peachpuff"sv, {255, 218, 185, 255}},
{"peru"sv, {205, 133, 63, 255}},
{"pink"sv, {255, 192, 203, 255}},
{"plum"sv, {221, 160, 221, 255}},
{"powderblue"sv, {176, 224, 230, 255}},
{"purple"sv, {128, 0, 128, 255}},
{"red"sv, {255, 0, 0, 255}},
{"rosybrown"sv, {188, 143, 143, 255}},
{"royalblue"sv, {65, 105, 225, 255}},
{"saddlebrown"sv, {139, 69, 19, 255}},
{"salmon"sv, {250, 128, 114, 255}},
{"sandybrown"sv, {244, 164, 96, 255}},
{"seagreen"sv, {46, 139, 87, 255}},
{"seashell"sv, {255, 245, 238, 255}},
{"sienna"sv, {160, 82, 45, 255}},
{"silver"sv, {192, 192, 192, 255}},
{"skyblue"sv, {135, 206, 235, 255}},
{"slateblue"sv, {106, 90, 205, 255}},
{"slategray"sv, {112, 128, 144, 255}},
{"slategrey"sv, {112, 128, 144, 255}},
{"snow"sv, {255, 250, 250, 255}},
{"springgreen"sv, {0, 255, 127, 255}},
{"steelblue"sv, {70, 130, 180, 255}},
{"tan"sv, {210, 180, 140, 255}},
{"teal"sv, {0, 128, 128, 255}},
{"thistle"sv, {216, 191, 216, 255}},
{"tomato"sv, {255, 99, 71, 255}},
{"turquoise"sv, {64, 224, 208, 255}},
{"violet"sv, {238, 130, 238, 255}},
{"wheat"sv, {245, 222, 179, 255}},
{"white"sv, {255, 255, 255, 255}},
{"whitesmoke"sv, {245, 245, 245, 255}},
{"yellow"sv, {255, 255, 0, 255}},
{"yellowgreen"sv, {154, 205, 50, 255}}}};
//-----------------------------------------------------------------------------
const CSSNamedColorArray& getCSSNamedColors () { return namedColors; }
} // VSTGUI
+246
View File
@@ -0,0 +1,246 @@
// 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
#pragma once
#include "vstguibase.h"
#include "vstguifwd.h"
#include <cmath>
#include <array>
#include <string_view>
namespace VSTGUI {
//-----------------------------------------------------------------------------
//! @brief RGBA Color structure
//-----------------------------------------------------------------------------
struct CColor
{
constexpr CColor () = default;
constexpr CColor (uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255)
: red (red), green (green), blue (blue), alpha (alpha)
{}
constexpr CColor (const CColor& inColor)
: red (inColor.red), green (inColor.green), blue (inColor.blue), alpha (inColor.alpha)
{}
//-----------------------------------------------------------------------------
/// @name Operator Methods
//-----------------------------------------------------------------------------
//@{
CColor& operator() (uint8_t _red, uint8_t _green, uint8_t _blue, uint8_t _alpha)
{
red = _red;
green = _green;
blue = _blue;
alpha = _alpha;
return *this;
}
CColor& operator= (const CColor& newColor)
{
red = newColor.red;
green = newColor.green;
blue = newColor.blue;
alpha = newColor.alpha;
return *this;
}
bool operator!= (const CColor &other) const
{ return (red != other.red || green != other.green || blue != other.blue || alpha != other.alpha); }
bool operator== (const CColor &other) const
{ return (red == other.red && green == other.green && blue == other.blue && alpha == other.alpha); }
//@}
//-----------------------------------------------------------------------------
/// @name Convert Methods
//-----------------------------------------------------------------------------
//@{
/**
* @brief convert to hue, saturation and value
* @param hue in degree [0..360]
* @param saturation normalized [0..1]
* @param value normalized [0..1]
*/
void toHSV (double& hue, double& saturation, double& value) const;
/**
* @brief convert from hue, saturation and value
* @param hue in degree [0..360]
* @param saturation normalized [0..1]
* @param value normalized [0..1]
*/
void fromHSV (double hue, double saturation, double value);
/**
* @brief convert to hue, saturation and lightness
* @param hue in degree [0..360]
* @param saturation normalized [0..1]
* @param lightness normalized [0..1]
*/
void toHSL (double& hue, double& saturation, double& lightness) const;
/**
* @brief convert from hue, saturation and lightness
* @param hue in degree [0..360]
* @param saturation normalized [0..1]
* @param lightness normalized [0..1]
*/
void fromHSL (double hue, double saturation, double lightness);
/** get the luma of the color */
inline constexpr uint8_t getLuma () const;
/** get the lightness of the color */
uint8_t getLightness () const;
/** get the normalized red value */
template<typename T>
constexpr T normRed () const;
/** get the normalized green value */
template<typename T>
constexpr T normGreen () const;
/** get the normalized blue value */
template<typename T>
constexpr T normBlue () const;
/** get the normalized alpha value */
template<typename T>
constexpr T normAlpha () const;
/** set the red value normalized */
template<typename T>
void setNormRed (T v);
/** set the green value normalized */
template<typename T>
void setNormGreen (T v);
/** set the blue value normalized */
template<typename T>
void setNormBlue (T v);
/** set the alpha value normalized */
template<typename T>
void setNormAlpha (T v);
//@}
UTF8String toString () const;
bool fromString (std::string_view str);
static bool isColorRepresentation (std::string_view str);
VSTGUI_DEPRECATED_MSG (bool fromString (UTF8StringPtr str);
, "use fromString with a std::string_view")
VSTGUI_DEPRECATED_MSG (static bool isColorRepresentation (UTF8StringPtr str);
, "use isColorRepresentation with a std::string_view")
/** red component [0..255] */
uint8_t red {255};
/** green component [0..255] */
uint8_t green {255};
/** blue component [0..255] */
uint8_t blue {255};
/** alpha component [0..255] */
uint8_t alpha {255};
};
//-----------------------------------------------------------------------------
inline constexpr CColor MakeCColor (uint8_t red = 0, uint8_t green = 0, uint8_t blue = 0,
uint8_t alpha = 255)
{
return CColor (red, green, blue, alpha);
}
//-----------------------------------------------------------------------------
// define some basic colors
constexpr const CColor kTransparentCColor = CColor (255, 255, 255, 0);
constexpr const CColor kBlackCColor = CColor ( 0, 0, 0, 255);
constexpr const CColor kWhiteCColor = CColor (255, 255, 255, 255);
constexpr const CColor kGreyCColor = CColor (127, 127, 127, 255);
constexpr const CColor kRedCColor = CColor (255, 0, 0, 255);
constexpr const CColor kGreenCColor = CColor ( 0, 255, 0, 255);
constexpr const CColor kBlueCColor = CColor ( 0, 0, 255, 255);
constexpr const CColor kYellowCColor = CColor (255, 255, 0, 255);
constexpr const CColor kMagentaCColor = CColor (255, 0, 255, 255);
constexpr const CColor kCyanCColor = CColor ( 0, 255, 255, 255);
//-----------------------------------------------------------------------------
// CSS Colors
struct CSSNamedColor
{
const std::string_view name;
const CColor color;
};
using CSSNamedColorArray = std::array<CSSNamedColor, 148>;
/** get the CSS color array
*
* @ingroup new_in_4_15
*/
const CSSNamedColorArray& getCSSNamedColors ();
//-----------------------------------------------------------------------------
inline constexpr uint8_t CColor::getLuma () const
{
return static_cast<uint8_t> (static_cast<float> (red) * 0.3f +
static_cast<float> (green) * 0.59f +
static_cast<float> (blue) * 0.11f);
}
//-----------------------------------------------------------------------------
template <typename T>
constexpr T CColor::normRed () const
{
return static_cast<T> (red) / static_cast<T> (255.);
}
//-----------------------------------------------------------------------------
template <typename T>
constexpr T CColor::normGreen () const
{
return static_cast<T> (green) / static_cast<T> (255.);
}
//-----------------------------------------------------------------------------
template <typename T>
constexpr T CColor::normBlue () const
{
return static_cast<T> (blue) / static_cast<T> (255.);
}
//-----------------------------------------------------------------------------
template <typename T>
constexpr T CColor::normAlpha () const
{
return static_cast<T> (alpha) / static_cast<T> (255.);
}
//-----------------------------------------------------------------------------
template <typename T>
void CColor::setNormRed (T v)
{
vstgui_assert (v >= 0. && v <= 1.);
red = static_cast<uint8_t> (std::round (v * 255.));
}
//-----------------------------------------------------------------------------
template <typename T>
void CColor::setNormGreen (T v)
{
vstgui_assert (v >= 0. && v <= 1.);
green = static_cast<uint8_t> (std::round (v * 255.));
}
//-----------------------------------------------------------------------------
template <typename T>
void CColor::setNormBlue (T v)
{
vstgui_assert (v >= 0. && v <= 1.);
blue = static_cast<uint8_t> (std::round (v * 255.));
}
//-----------------------------------------------------------------------------
template <typename T>
void CColor::setNormAlpha (T v)
{
vstgui_assert (v >= 0. && v <= 1.);
alpha = static_cast<uint8_t> (std::round (v * 255.));
}
} // VSTGUI
File diff suppressed because it is too large Load Diff
+128
View File
@@ -0,0 +1,128 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "cscrollview.h"
#include "cfont.h"
#include "ccolor.h"
#include "cstring.h"
#include <vector>
namespace VSTGUI {
// forward private internal views
class CDataBrowserView;
class CDataBrowserHeader;
//-----------------------------------------------------------------------------
// CDataBrowser Declaration
//! @brief DataBrowser view
/// @ingroup controls
//-----------------------------------------------------------------------------------------------
class CDataBrowser : public CScrollView
{
protected:
enum
{
kDrawRowLinesFlag = kLastScrollViewStyleFlag,
kDrawColumnLinesFlag,
kDrawHeaderFlag,
kMultiSelectionStyleFlag
};
public:
CDataBrowser (const CRect& size, IDataBrowserDelegate* db, int32_t style = 0, CCoord scrollbarWidth = 16, CBitmap* pBackground = nullptr);
enum CDataBrowserStyle
{
// see CScrollView for more styles
kDrawRowLines = 1 << kDrawRowLinesFlag,
kDrawColumnLines = 1 << kDrawColumnLinesFlag,
kDrawHeader = 1 << kDrawHeaderFlag,
kMultiSelectionStyle = 1 << kMultiSelectionStyleFlag
};
enum
{
kNoSelection = -1
};
/// @brief CDataBrowser Cell position description
struct Cell {
int32_t row {-1};
int32_t column {-1};
Cell () = default;
Cell (int32_t row, int32_t column) : row (row), column (column) {}
bool isValid () const { return row > -1 && column > -1; }
};
using Selection = std::vector<int32_t>;
//-----------------------------------------------------------------------------
/// @name CDataBrowser Methods
//-----------------------------------------------------------------------------
//@{
/** trigger recalculation, call if numRows or numColumns changed */
virtual void recalculateLayout (bool rememberSelection = false);
/** invalidates an individual cell */
virtual void invalidate (const Cell& cell);
/** invalidates a complete row */
virtual void invalidateRow (int32_t row);
/** scrolls the scrollview so that row is visible */
virtual void makeRowVisible (int32_t row);
/** get bounds of a cell */
virtual CRect getCellBounds (const Cell& cell);
/** get the cell at position where */
virtual Cell getCellAt (const CPoint& where) const;
/** get first selected row */
virtual int32_t getSelectedRow () const;
/** set the exclusive selected row */
virtual void setSelectedRow (int32_t row, bool makeVisible = false);
/** get all selected rows */
const Selection& getSelection () const { return selection; }
/** add row to selection */
virtual void selectRow (int32_t row);
/** remove row from selection */
virtual void unselectRow (int32_t row);
/** empty selection */
virtual void unselectAll ();
/** starts a text edit for a cell */
virtual void beginTextEdit (const Cell& cell, UTF8StringPtr initialText);
/** get delegate object */
IDataBrowserDelegate* getDelegate () const { return db; }
//@}
void setAutosizeFlags (int32_t flags) override;
void setViewSize (const CRect& size, bool invalid) override;
void setWantsFocus (bool state) override;
void onKeyboardEvent (KeyboardEvent& event) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
protected:
~CDataBrowser () noexcept override;
void valueChanged (CControl *pControl) override;
CMessageResult notify (CBaseObject* sender, IdStringPtr message) override;
bool attached (CView *parent) override;
bool removed (CView* parent) override;
bool wantsFocus () const override;
void validateSelection ();
IDataBrowserDelegate* db;
CDataBrowserView* dbView;
CDataBrowserHeader* dbHeader;
CViewContainer* dbHeaderContainer;
Selection selection;
};
//-----------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,779 @@
// 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 "cdrawcontext.h"
#include "cgraphicspath.h"
#include "cgradient.h"
#include "cbitmap.h"
#include "cstring.h"
#include "platform/iplatformgraphicsdevice.h"
#include "platform/iplatformfont.h"
#include "platform/iplatformgraphicspath.h"
#include "platform/iplatformgradient.h"
#include "platform/platformfactory.h"
#include <cassert>
#include <stack>
namespace VSTGUI {
//-----------------------------------------------------------------------------
CDrawContext::Transform::Transform (CDrawContext& context, const CGraphicsTransform& transformation)
: context (context)
, transformation (transformation)
{
if (transformation.isInvariant () == false)
context.pushTransform (transformation);
}
//-----------------------------------------------------------------------------
CDrawContext::Transform::~Transform () noexcept
{
if (transformation.isInvariant () == false)
context.popTransform ();
}
//-----------------------------------------------------------------------------
struct CDrawContext::Impl
{
//-----------------------------------------------------------------------------
struct State
{
SharedPointer<CFontDesc> font;
CColor frameColor {kTransparentCColor};
CColor fillColor {kTransparentCColor};
CColor fontColor {kTransparentCColor};
CCoord frameWidth {0.};
CPoint penLoc {};
CRect clipRect {};
CLineStyle lineStyle {kLineOnOffDash};
CDrawMode drawMode {kAntiAliasing};
float globalAlpha {1.f};
BitmapInterpolationQuality bitmapQuality {BitmapInterpolationQuality::kDefault};
State () = default;
State (const State& state);
State& operator= (const State& state) = default;
State (State&& state) noexcept;
State& operator= (State&& state) noexcept;
};
UTF8String* drawStringHelper {nullptr};
CRect surfaceRect;
double scaleFactor {1.};
State currentState;
std::stack<State> globalStatesStack;
std::stack<CGraphicsTransform> transformStack;
PlatformGraphicsDeviceContextPtr device;
};
//-----------------------------------------------------------------------------
CDrawContext::Impl::State::State (const State& state) { *this = state; }
//-----------------------------------------------------------------------------
CDrawContext::Impl::State::State (State&& state) noexcept { *this = std::move (state); }
//-----------------------------------------------------------------------------
CDrawContext::Impl::State&
CDrawContext::Impl::State::operator= (CDrawContext::Impl::State&& state) noexcept
{
font = std::move (state.font);
frameColor = std::move (state.frameColor);
fillColor = std::move (state.fillColor);
fontColor = std::move (state.fontColor);
frameWidth = std::move (state.frameWidth);
penLoc = std::move (state.penLoc);
clipRect = std::move (state.clipRect);
lineStyle = std::move (state.lineStyle);
drawMode = std::move (state.drawMode);
globalAlpha = std::move (state.globalAlpha);
return *this;
}
//-----------------------------------------------------------------------------
CDrawContext::CDrawContext (const CRect& surfaceRect)
{
impl = std::make_unique<Impl> ();
impl->surfaceRect = surfaceRect;
impl->transformStack.push (CGraphicsTransform ());
}
//------------------------------------------------------------------------
CDrawContext::CDrawContext (const PlatformGraphicsDeviceContextPtr device, const CRect& surfaceRect,
double scaleFactor)
: CDrawContext (surfaceRect)
{
impl->device = device;
impl->scaleFactor = scaleFactor;
setClipRect (surfaceRect);
}
//-----------------------------------------------------------------------------
CDrawContext::~CDrawContext () noexcept
{
#if DEBUG
if (!impl->globalStatesStack.empty ())
DebugPrint ("Global state stack not empty. Save and restore global state must be called in sequence !\n");
#endif
if (impl->drawStringHelper)
delete impl->drawStringHelper;
}
//------------------------------------------------------------------------
const PlatformGraphicsDeviceContextPtr& CDrawContext::getPlatformDeviceContext () const
{
return impl->device;
}
//-----------------------------------------------------------------------------
const CRect& CDrawContext::getSurfaceRect () const { return impl->surfaceRect; }
//------------------------------------------------------------------------
double CDrawContext::getScaleFactor () const { return impl->scaleFactor; }
//-----------------------------------------------------------------------------
void CDrawContext::init ()
{
// set the default values
setFrameColor (kWhiteCColor);
setLineStyle (kLineSolid);
setLineWidth (1);
setFillColor (kBlackCColor);
setFontColor (kWhiteCColor);
setFont (kSystemFont);
setDrawMode (kAliasing);
setClipRect (impl->surfaceRect);
}
//-----------------------------------------------------------------------------
void CDrawContext::saveGlobalState ()
{
impl->globalStatesStack.push (impl->currentState);
if (impl->device)
impl->device->saveGlobalState ();
}
//-----------------------------------------------------------------------------
void CDrawContext::restoreGlobalState ()
{
if (impl->device)
impl->device->restoreGlobalState ();
if (!impl->globalStatesStack.empty ())
{
impl->currentState = std::move (impl->globalStatesStack.top ());
impl->globalStatesStack.pop ();
}
else
{
#if DEBUG
DebugPrint ("No saved global state in draw context !!!\n");
#endif
}
}
//-----------------------------------------------------------------------------
void CDrawContext::setBitmapInterpolationQuality (BitmapInterpolationQuality quality)
{
impl->currentState.bitmapQuality = quality;
}
//-----------------------------------------------------------------------------
BitmapInterpolationQuality CDrawContext::getBitmapInterpolationQuality () const
{
return impl->currentState.bitmapQuality;
}
//-----------------------------------------------------------------------------
void CDrawContext::setLineStyle (const CLineStyle& style)
{
if (impl->device)
impl->device->setLineStyle (style);
impl->currentState.lineStyle = style;
}
//-----------------------------------------------------------------------------
const CLineStyle& CDrawContext::getLineStyle () const { return impl->currentState.lineStyle; }
//-----------------------------------------------------------------------------
void CDrawContext::setLineWidth (CCoord width)
{
if (impl->device)
impl->device->setLineWidth (width);
impl->currentState.frameWidth = width;
}
//-----------------------------------------------------------------------------
CCoord CDrawContext::getLineWidth () const { return impl->currentState.frameWidth; }
//-----------------------------------------------------------------------------
void CDrawContext::setDrawMode (CDrawMode mode)
{
if (impl->device)
impl->device->setDrawMode (mode);
impl->currentState.drawMode = mode;
}
//-----------------------------------------------------------------------------
CDrawMode CDrawContext::getDrawMode () const { return impl->currentState.drawMode; }
//-----------------------------------------------------------------------------
CRect& CDrawContext::getClipRect (CRect &clip) const
{
clip = impl->currentState.clipRect;
getCurrentTransform ().inverse ().transform (clip);
clip.normalize ();
return clip;
}
//-----------------------------------------------------------------------------
const CRect& CDrawContext::getAbsoluteClipRect () const { return impl->currentState.clipRect; }
//-----------------------------------------------------------------------------
void CDrawContext::setClipRect (const CRect &clip)
{
impl->currentState.clipRect = clip;
getCurrentTransform ().transform (impl->currentState.clipRect);
impl->currentState.clipRect.normalize ();
if (impl->device)
impl->device->setClipRect (impl->currentState.clipRect);
}
//-----------------------------------------------------------------------------
void CDrawContext::resetClipRect ()
{
if (impl->device)
impl->device->setClipRect (getSurfaceRect ());
impl->currentState.clipRect = getSurfaceRect ();
}
//-----------------------------------------------------------------------------
void CDrawContext::setFillColor (const CColor& color)
{
if (impl->device)
impl->device->setFillColor (color);
impl->currentState.fillColor = color;
}
//-----------------------------------------------------------------------------
CColor CDrawContext::getFillColor () const { return impl->currentState.fillColor; }
//-----------------------------------------------------------------------------
void CDrawContext::setFrameColor (const CColor& color)
{
if (impl->device)
impl->device->setFrameColor (color);
impl->currentState.frameColor = color;
}
//-----------------------------------------------------------------------------
CColor CDrawContext::getFrameColor () const { return impl->currentState.frameColor; }
//-----------------------------------------------------------------------------
void CDrawContext::setFontColor (const CColor& color) { impl->currentState.fontColor = color; }
//-----------------------------------------------------------------------------
CColor CDrawContext::getFontColor () const { return impl->currentState.fontColor; }
//-----------------------------------------------------------------------------
void CDrawContext::setFont (const CFontRef newFont, const CCoord& size, const int32_t& style)
{
if (newFont == nullptr)
return;
if ((size > 0 && newFont->getSize () != size) || (style != -1 && newFont->getStyle () != style))
{
impl->currentState.font = makeOwned<CFontDesc> (*newFont);
if (size > 0)
impl->currentState.font->setSize (size);
if (style != -1)
impl->currentState.font->setStyle (style);
}
else
{
impl->currentState.font = newFont;
}
}
//-----------------------------------------------------------------------------
const CFontRef CDrawContext::getFont () const { return impl->currentState.font; }
//-----------------------------------------------------------------------------
void CDrawContext::setGlobalAlpha (float newAlpha)
{
if (impl->device)
impl->device->setGlobalAlpha (newAlpha);
impl->currentState.globalAlpha = newAlpha;
}
//-----------------------------------------------------------------------------
float CDrawContext::getGlobalAlpha () const { return impl->currentState.globalAlpha; }
//-----------------------------------------------------------------------------
const UTF8String& CDrawContext::getDrawString (UTF8StringPtr string)
{
if (impl->drawStringHelper == nullptr)
impl->drawStringHelper = new UTF8String (string);
else
impl->drawStringHelper->assign (string);
return *impl->drawStringHelper;
}
//-----------------------------------------------------------------------------
void CDrawContext::clearDrawString ()
{
if (impl->drawStringHelper)
impl->drawStringHelper->clear ();
}
//------------------------------------------------------------------------
CCoord CDrawContext::getStringWidth (IPlatformString* string)
{
CCoord result = -1;
if (impl->currentState.font == nullptr || string == nullptr)
return result;
if (auto painter = impl->currentState.font->getFontPainter ())
result = painter->getStringWidth (impl->device, string, true);
return result;
}
//------------------------------------------------------------------------
void CDrawContext::drawString (IPlatformString* string, const CRect& _rect, const CHoriTxtAlign hAlign, bool antialias)
{
if (!string || impl->currentState.font == nullptr)
return;
auto painter = impl->currentState.font->getFontPainter ();
if (painter == nullptr)
return;
CRect rect (_rect);
double capHeight = -1;
auto platformFont = impl->currentState.font->getPlatformFont ();
if (platformFont)
capHeight = platformFont->getCapHeight ();
if (capHeight > 0.)
rect.bottom -= (rect.getHeight () / 2. - capHeight / 2.);
else
rect.bottom -= (rect.getHeight () / 2. - impl->currentState.font->getSize () / 2.) + 1.;
if (hAlign != kLeftText)
{
CCoord stringWidth = painter->getStringWidth (impl->device, string, antialias);
if (hAlign == kRightText)
rect.left = rect.right - stringWidth;
else
rect.left = rect.left + (rect.getWidth () / 2.) - (stringWidth / 2.);
}
painter->drawString (impl->device, string, CPoint (rect.left, rect.bottom),
impl->currentState.fontColor, antialias);
}
//------------------------------------------------------------------------
void CDrawContext::drawString (IPlatformString* string, const CPoint& point, bool antialias)
{
if (string == nullptr || impl->currentState.font == nullptr)
return;
if (auto painter = impl->currentState.font->getFontPainter ())
painter->drawString (impl->device, string, point, impl->currentState.fontColor, antialias);
}
//-----------------------------------------------------------------------------
CCoord CDrawContext::getStringWidth (UTF8StringPtr string)
{
return getStringWidth (getDrawString (string).getPlatformString ());
}
//-----------------------------------------------------------------------------
void CDrawContext::drawString (UTF8StringPtr string, const CPoint& point, bool antialias)
{
drawString (getDrawString (string).getPlatformString (), point, antialias);
clearDrawString ();
}
//-----------------------------------------------------------------------------
void CDrawContext::drawString (UTF8StringPtr string, const CRect& rect, const CHoriTxtAlign hAlign, bool antialias)
{
drawString (getDrawString (string).getPlatformString (), rect, hAlign, antialias);
clearDrawString ();
}
//-----------------------------------------------------------------------------
void CDrawContext::fillRectWithBitmap (CBitmap* bitmap, const CRect& srcRect, const CRect& dstRect, float alpha)
{
if (srcRect.isEmpty () || dstRect.isEmpty ())
return;
if (srcRect.getWidth () == dstRect.getWidth () && srcRect.getHeight () == dstRect.getHeight ())
{
drawBitmap (bitmap, dstRect, srcRect.getTopLeft (), alpha);
return;
}
if (impl->device)
{
if (auto deviceBitmapExt = impl->device->asBitmapExt ())
{
double transformedScaleFactor = getScaleFactor ();
CGraphicsTransform t = getCurrentTransform ();
if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0)
transformedScaleFactor *= t.m11;
if (auto pb = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor))
{
if (deviceBitmapExt->fillRectWithBitmap (*pb, srcRect, dstRect, alpha,
getBitmapInterpolationQuality ()))
{
return;
}
}
}
}
CRect bitmapPartRect;
CPoint sourceOffset (srcRect.left, srcRect.top);
for (auto top = dstRect.top; top < dstRect.bottom; top += srcRect.getHeight ())
{
bitmapPartRect.top = top;
bitmapPartRect.bottom = top + srcRect.getHeight ();
if (bitmapPartRect.bottom > dstRect.bottom)
bitmapPartRect.bottom = dstRect.bottom;
// The following should never be true, I guess
if (bitmapPartRect.getHeight () > srcRect.getHeight ())
bitmapPartRect.setHeight (srcRect.getHeight ());
for (auto left = dstRect.left; left < dstRect.right; left += srcRect.getWidth ())
{
bitmapPartRect.left = left;
bitmapPartRect.right = left + srcRect.getWidth ();
if (bitmapPartRect.right > dstRect.right)
bitmapPartRect.right = dstRect.right;
// The following should never be true, I guess
if (bitmapPartRect.getWidth () > srcRect.getWidth ())
bitmapPartRect.setWidth (srcRect.getWidth ());
drawBitmap (bitmap, bitmapPartRect, sourceOffset, alpha);
}
}
}
//-----------------------------------------------------------------------------
void CDrawContext::drawBitmapNinePartTiled (CBitmap* bitmap, const CRect& dest, const CNinePartTiledDescription& desc, float alpha)
{
if (impl->device)
{
if (auto deviceBitmapExt = impl->device->asBitmapExt ())
{
double transformedScaleFactor = getScaleFactor ();
CGraphicsTransform t = getCurrentTransform ();
if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0)
transformedScaleFactor *= t.m11;
if (auto pb = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor))
{
if (deviceBitmapExt->drawBitmapNinePartTiled (*pb, dest, desc, alpha,
getBitmapInterpolationQuality ()))
{
return;
}
}
}
}
CRect myBitmapBounds (0, 0, bitmap->getWidth (), bitmap->getHeight ());
CRect mySourceRect [CNinePartTiledDescription::kPartCount];
CRect myDestRect [CNinePartTiledDescription::kPartCount];
desc.calcRects (myBitmapBounds, mySourceRect);
desc.calcRects (dest, myDestRect);
for (size_t i = 0; i < CNinePartTiledDescription::kPartCount; i++)
fillRectWithBitmap (bitmap, mySourceRect[i], myDestRect[i], alpha);
}
//-----------------------------------------------------------------------------
CGraphicsPath* CDrawContext::createRoundRectGraphicsPath (const CRect& size, CCoord radius)
{
if (auto path = createGraphicsPath ())
{
path->addRoundRect (size, radius);
return path;
}
return {};
}
//-----------------------------------------------------------------------------
void CDrawContext::pushTransform (const CGraphicsTransform& transformation)
{
vstgui_assert (!impl->transformStack.empty ());
const CGraphicsTransform& currentTransform = impl->transformStack.top ();
CGraphicsTransform newTransform = currentTransform * transformation;
impl->transformStack.push (newTransform);
if (impl->device)
impl->device->setTransformMatrix (newTransform);
}
//-----------------------------------------------------------------------------
void CDrawContext::popTransform ()
{
vstgui_assert (impl->transformStack.size () > 1);
impl->transformStack.pop ();
if (impl->device)
impl->device->setTransformMatrix (impl->transformStack.top ());
}
//-----------------------------------------------------------------------------
const CGraphicsTransform& CDrawContext::getCurrentTransform () const
{
return impl->transformStack.top ();
}
//------------------------------------------------------------------------
CCoord CDrawContext::getHairlineSize () const
{
return 1. / (getScaleFactor () * getCurrentTransform ().m11);
}
//------------------------------------------------------------------------
static PlatformGraphicsDrawStyle convert (CDrawStyle s)
{
switch (s)
{
case CDrawStyle::kDrawFilled:
return PlatformGraphicsDrawStyle::Filled;
case CDrawStyle::kDrawStroked:
return PlatformGraphicsDrawStyle::Stroked;
case CDrawStyle::kDrawFilledAndStroked:
return PlatformGraphicsDrawStyle::FilledAndStroked;
default:
assert (false);
}
return {};
}
//------------------------------------------------------------------------
void CDrawContext::drawLine (const LinePair& line)
{
if (impl->device)
impl->device->drawLine (line);
}
//------------------------------------------------------------------------
void CDrawContext::drawLines (const LineList& lines)
{
if (impl->device)
impl->device->drawLines (lines);
}
//------------------------------------------------------------------------
void CDrawContext::drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle)
{
if (impl->device)
impl->device->drawPolygon (polygonPointList, convert (drawStyle));
}
//------------------------------------------------------------------------
void CDrawContext::drawRect (const CRect& rect, const CDrawStyle drawStyle)
{
if (impl->device)
impl->device->drawRect (rect, convert (drawStyle));
}
//------------------------------------------------------------------------
void CDrawContext::drawArc (const CRect& rect, const float startAngle1, const float endAngle2,
const CDrawStyle drawStyle)
{
if (impl->device)
impl->device->drawArc (rect, startAngle1, endAngle2, convert (drawStyle));
}
//------------------------------------------------------------------------
void CDrawContext::drawEllipse (const CRect& rect, const CDrawStyle drawStyle)
{
if (impl->device)
impl->device->drawEllipse (rect, convert (drawStyle));
}
//------------------------------------------------------------------------
void CDrawContext::drawPoint (const CPoint& point, const CColor& color)
{
if (impl->device && impl->device->drawPoint (point, color))
return;
// if the platform does not support drawing points, emulate it somehow
saveGlobalState ();
CRect r (point.x, point.y, point.x, point.y);
r.inset (-0.5, -0.5);
setDrawMode (kAliasing);
setFillColor (color);
drawRect (r, kDrawFilled);
restoreGlobalState ();
}
//------------------------------------------------------------------------
void CDrawContext::drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset,
float alpha)
{
if (impl->device)
{
double transformedScaleFactor = getScaleFactor ();
CGraphicsTransform t = getCurrentTransform ();
if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0)
transformedScaleFactor *= t.m11;
if (auto pb = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor))
impl->device->drawBitmap (*pb, dest, offset, alpha, getBitmapInterpolationQuality ());
}
}
//------------------------------------------------------------------------
void CDrawContext::clearRect (const CRect& rect)
{
if (impl->device)
impl->device->clearRect (rect);
}
//------------------------------------------------------------------------
static PlatformGraphicsPathDrawMode convert (CDrawContext::PathDrawMode mode)
{
switch (mode)
{
case CDrawContext::PathDrawMode::kPathFilled:
return PlatformGraphicsPathDrawMode::Filled;
case CDrawContext::PathDrawMode::kPathStroked:
return PlatformGraphicsPathDrawMode::Stroked;
case CDrawContext::PathDrawMode::kPathFilledEvenOdd:
return PlatformGraphicsPathDrawMode::FilledEvenOdd;
default:
assert (false);
}
return {};
}
//------------------------------------------------------------------------
void CDrawContext::drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode,
CGraphicsTransform* transformation)
{
if (impl->device)
{
if (auto& pp = path->getPlatformPath (mode == kPathFilledEvenOdd
? PlatformGraphicsPathFillMode::Alternate
: PlatformGraphicsPathFillMode::Winding))
impl->device->drawGraphicsPath (*pp.get (), convert (mode), transformation);
}
}
//------------------------------------------------------------------------
void CDrawContext::fillLinearGradient (CGraphicsPath* path, const CGradient& gradient,
const CPoint& startPoint, const CPoint& endPoint,
bool evenOdd, CGraphicsTransform* transformation)
{
if (impl->device)
{
if (auto& platformGradient = gradient.getPlatformGradient ())
{
if (auto& pp = path->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate
: PlatformGraphicsPathFillMode::Winding))
{
impl->device->fillLinearGradient (*pp.get (), *platformGradient, startPoint,
endPoint, evenOdd, transformation);
}
}
}
}
//------------------------------------------------------------------------
void CDrawContext::fillRadialGradient (CGraphicsPath* path, const CGradient& gradient,
const CPoint& center, CCoord radius,
const CPoint& originOffset, bool evenOdd,
CGraphicsTransform* transformation)
{
if (impl->device)
{
if (auto& platformGradient = gradient.getPlatformGradient ())
{
if (auto& pp = path->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate
: PlatformGraphicsPathFillMode::Winding))
{
impl->device->fillRadialGradient (*pp.get (), *platformGradient, center, radius,
originOffset, evenOdd, transformation);
}
}
}
}
//------------------------------------------------------------------------
bool CDrawContext::drawLinearGradientLine (const DrawLinearGradientLineCallback& cb)
{
if (auto ext =
std::dynamic_pointer_cast<IPlatformGraphicsDeviceContextGradientExt> (impl->device))
{
struct Tmp : IDrawLinearGradientLine
{
std::shared_ptr<IPlatformGraphicsDeviceContextGradientExt> obj;
bool draw (const PointList& line, const CGradient& gradient, CCoord lineWidth,
CLineStyle::LineCap cap, CLineStyle::LineJoin join) const override
{
const auto& platformGradient = gradient.getPlatformGradient ();
if (!platformGradient)
return false;
return obj->drawLinearGradientLine (line, *gradient.getPlatformGradient ().get (),
lineWidth, static_cast<LineCap> (cap),
static_cast<LineJoin> (join));
}
};
Tmp t;
t.obj = ext;
cb (t);
return true;
}
return false;
}
//------------------------------------------------------------------------
CGraphicsPath* CDrawContext::createGraphicsPath ()
{
if (impl->device)
return new CGraphicsPath (impl->device->getGraphicsPathFactory (), nullptr);
return nullptr;
}
//------------------------------------------------------------------------
CGraphicsPath* CDrawContext::createTextPath (const CFontRef font, UTF8StringPtr text)
{
if (impl->device)
{
auto platformFont = font->getPlatformFont ();
auto pathFactory = impl->device->getGraphicsPathFactory ();
if (platformFont && pathFactory)
{
if (auto textPath = pathFactory->createTextPath (platformFont, text))
return new CGraphicsPath (pathFactory, std::move (textPath));
}
}
return nullptr;
}
//------------------------------------------------------------------------
void CDrawContext::beginDraw ()
{
if (impl->device)
impl->device->beginDraw ();
}
//------------------------------------------------------------------------
void CDrawContext::endDraw ()
{
if (impl->device)
impl->device->endDraw ();
}
} // VSTGUI
+311
View File
@@ -0,0 +1,311 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "cpoint.h"
#include "crect.h"
#include "cfont.h"
#include "ccolor.h"
#include "cgraphicstransform.h"
#include "clinestyle.h"
#include "cdrawdefs.h"
#include <cmath>
#include <vector>
namespace VSTGUI {
struct CNinePartTiledDescription;
//-----------------------------------------------------------------------------
// CDrawContext Declaration
//! @brief A drawing context encapsulates the drawing context of the underlying OS
//-----------------------------------------------------------------------------
class CDrawContext : public AtomicReferenceCounted
{
public:
//-----------------------------------------------------------------------------
/** Add a transform to all draw routines. Must be used as stack object. */
//-----------------------------------------------------------------------------
struct Transform
{
Transform (CDrawContext& context, const CGraphicsTransform& transformation);
~Transform () noexcept;
private:
CDrawContext& context;
const CGraphicsTransform transformation;
};
//-----------------------------------------------------------------------------
/// @name Draw primitives
//-----------------------------------------------------------------------------
//@{
using LinePair = VSTGUI::LinePair;
using LineList = VSTGUI::LineList;
using PointList = VSTGUI::PointList;
inline void drawLine (const CPoint& start, const CPoint& end)
{
drawLine (std::make_pair (start, end));
}
/** draw a line */
void drawLine (const LinePair& line);
/** draw multiple lines at once */
void drawLines (const LineList& lines);
/** draw a polygon */
void drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle = kDrawStroked);
/** draw a rect */
void drawRect (const CRect& rect, const CDrawStyle drawStyle = kDrawStroked);
/** draw an arc, angles are in degree */
void drawArc (const CRect& rect, const float startAngle1, const float endAngle2,
const CDrawStyle drawStyle = kDrawStroked);
/** draw an ellipse */
void drawEllipse (const CRect& rect, const CDrawStyle drawStyle = kDrawStroked);
/** draw a point */
void drawPoint (const CPoint& point, const CColor& color);
/** don't call directly, please use CBitmap::draw instead */
void drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset = CPoint (0, 0),
float alpha = 1.f);
void drawBitmapNinePartTiled (CBitmap* bitmap, const CRect& dest,
const CNinePartTiledDescription& desc, float alpha = 1.f);
void fillRectWithBitmap (CBitmap* bitmap, const CRect& srcRect, const CRect& dstRect,
float alpha);
/** clears the rect (makes r = 0, g = 0, b = 0, a = 0) */
void clearRect (const CRect& rect);
//@}
//-----------------------------------------------------------------------------
// @name Bitmap Interpolation Quality
//-----------------------------------------------------------------------------
//@{
/** set the current bitmap interpolation quality */
void setBitmapInterpolationQuality (BitmapInterpolationQuality quality);
/** get the current bitmap interpolation quality */
BitmapInterpolationQuality getBitmapInterpolationQuality () const;
//@}
//-----------------------------------------------------------------------------
/// @name Line Mode
//-----------------------------------------------------------------------------
//@{
/** set the current line style */
void setLineStyle (const CLineStyle& style);
/** get the current line style */
const CLineStyle& getLineStyle () const;
/** set the current line width */
void setLineWidth (CCoord width);
/** get the current line width */
CCoord getLineWidth () const;
//@}
//-----------------------------------------------------------------------------
/// @name Draw Mode
//-----------------------------------------------------------------------------
//@{
/** set the current draw mode, see CDrawMode */
void setDrawMode (CDrawMode mode);
/** get the current draw mode, see CDrawMode */
CDrawMode getDrawMode () const;
//@}
//-----------------------------------------------------------------------------
/// @name Clipping
//-----------------------------------------------------------------------------
//@{
/** set the current clip */
void setClipRect (const CRect& clip);
/** get the current clip */
CRect& getClipRect (CRect &clip) const;
/** reset the clip to the default state */
void resetClipRect ();
//@}
//-----------------------------------------------------------------------------
/// @name Color
//-----------------------------------------------------------------------------
//@{
/** set current fill color */
void setFillColor (const CColor& color);
/** get current fill color */
CColor getFillColor () const;
/** set current stroke color */
void setFrameColor (const CColor& color);
/** get current stroke color */
CColor getFrameColor () const;
//@}
//-----------------------------------------------------------------------------
/// @name Font
//-----------------------------------------------------------------------------
//@{
/** set current font color */
void setFontColor (const CColor& color);
/** get current font color */
CColor getFontColor () const;
/** set current font */
void setFont (const CFontRef font, const CCoord& size = 0, const int32_t& style = -1);
/** get current font */
const CFontRef getFont () const;
//@}
//-----------------------------------------------------------------------------
/// @name Text
//-----------------------------------------------------------------------------
//@{
/** get the width of an UTF-8 encoded string */
CCoord getStringWidth (UTF8StringPtr pStr);
/** draw an UTF-8 encoded string */
void drawString (UTF8StringPtr string, const CRect& _rect,
const CHoriTxtAlign hAlign = kCenterText, bool antialias = true);
/** draw an UTF-8 encoded string */
void drawString (UTF8StringPtr string, const CPoint& _point, bool antialias = true);
/** get the width of a platform string */
CCoord getStringWidth (IPlatformString* pStr);
/** draw a platform string */
void drawString (IPlatformString* string, const CRect& _rect,
const CHoriTxtAlign hAlign = kCenterText, bool antialias = true);
/** draw a platform string */
void drawString (IPlatformString* string, const CPoint& _point, bool antialias = true);
//@}
//-----------------------------------------------------------------------------
/// @name Global Alpha State
//-----------------------------------------------------------------------------
//@{
/** sets the global alpha value[0..1] */
void setGlobalAlpha (float newAlpha);
/** get current global alpha value */
float getGlobalAlpha () const;
//@}
//-----------------------------------------------------------------------------
/// @name Global State Stack
//-----------------------------------------------------------------------------
//@{
void saveGlobalState ();
void restoreGlobalState ();
//@}
//-----------------------------------------------------------------------------
/// @name Transformation
//-----------------------------------------------------------------------------
//@{
const CGraphicsTransform& getCurrentTransform () const;
const CRect& getAbsoluteClipRect () const;
/** returns the backend scale factor. */
double getScaleFactor () const;
/** returns the current line size which corresponds to one pixel on screen.
*
* do not cache this value, instead ask for it every time you need it.
*/
CCoord getHairlineSize () const;
//@}
//-----------------------------------------------------------------------------
/// @name Graphics Paths
//-----------------------------------------------------------------------------
//@{
/** create a graphics path object, you need to forget it after usage */
CGraphicsPath* createGraphicsPath ();
/** create a graphics path from a text */
CGraphicsPath* createTextPath (const CFontRef font, UTF8StringPtr text);
/** create a rect with round corners as graphics path, you need to forget it after usage */
CGraphicsPath* createRoundRectGraphicsPath (const CRect& size, CCoord radius);
enum PathDrawMode
{
kPathFilled,
kPathFilledEvenOdd,
kPathStroked
};
void drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode = kPathFilled,
CGraphicsTransform* transformation = nullptr);
void fillLinearGradient (CGraphicsPath* path, const CGradient& gradient,
const CPoint& startPoint, const CPoint& endPoint, bool evenOdd = false,
CGraphicsTransform* transformation = nullptr);
void fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, const CPoint& center,
CCoord radius, const CPoint& originOffset = CPoint (0, 0),
bool evenOdd = false, CGraphicsTransform* transformation = nullptr);
//@}
struct IDrawLinearGradientLine
{
virtual bool draw (const PointList& line, const CGradient& gradient, CCoord lineWidth,
CLineStyle::LineCap, CLineStyle::LineJoin) const = 0;
};
using DrawLinearGradientLineCallback = std::function<void (IDrawLinearGradientLine&)>;
bool drawLinearGradientLine (const DrawLinearGradientLineCallback& cb);
void beginDraw ();
void endDraw ();
const CRect& getSurfaceRect () const;
CDrawContext (const PlatformGraphicsDeviceContextPtr device, const CRect& surfaceRect,
double scaleFactor);
~CDrawContext () noexcept override;
const PlatformGraphicsDeviceContextPtr& getPlatformDeviceContext () const;
protected:
CDrawContext () = delete;
explicit CDrawContext (const CRect& surfaceRect);
void init ();
void pushTransform (const CGraphicsTransform& transformation);
void popTransform ();
const UTF8String& getDrawString (UTF8StringPtr string);
void clearDrawString ();
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
//-----------------------------------------------------------------------------
struct ConcatClip
{
ConcatClip (CDrawContext& context, const CRect& rect)
: context (context), newClip (rect)
{
context.getClipRect (origClip);
newClip.normalize ();
newClip.bound (origClip);
context.setClipRect (newClip);
}
~ConcatClip () noexcept
{
context.setClipRect (origClip);
}
bool isEmpty () const { return newClip.isEmpty (); }
const CRect& get () const { return newClip; }
private:
CDrawContext& context;
CRect origClip;
CRect newClip;
};
//-----------------------------------------------------------------------------
template<typename Proc>
void drawClipped (CDrawContext* context, const CRect& clip, Proc proc)
{
ConcatClip cc (*context, clip);
if (!cc.isEmpty ())
proc ();
}
} // VSTGUI
+68
View File
@@ -0,0 +1,68 @@
// 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
#pragma once
#include "vstguifwd.h"
namespace VSTGUI {
//-----------
// @brief Draw Mode Flags
//-----------
enum CDrawModeFlags : uint32_t
{
/** aliased drawing */
kAliasing = 0,
/** antialised drawing */
kAntiAliasing = 1,
/** do not round coordinates to pixel aligned values */
kNonIntegralMode = 0xF0000000
};
//-----------
// @brief Draw Mode
//-----------
struct CDrawMode
{
public:
constexpr CDrawMode (uint32_t mode = kAliasing) : mode (mode) {}
constexpr CDrawMode (const CDrawMode& m) = default;
constexpr CDrawMode& operator= (const CDrawMode& m) = default;
constexpr uint32_t modeIgnoringIntegralMode () const { return (mode & ~kNonIntegralMode); }
constexpr bool integralMode () const { return !hasBit (mode, kNonIntegralMode); }
constexpr bool aliasing () const { return modeIgnoringIntegralMode () == kAliasing; }
constexpr bool antiAliasing () const { return modeIgnoringIntegralMode () == kAntiAliasing; }
CDrawMode& operator= (uint32_t m) { mode = m; return *this; }
constexpr uint32_t operator() () const { return mode; }
constexpr bool operator== (const CDrawMode& m) const { return modeIgnoringIntegralMode () == m.modeIgnoringIntegralMode (); }
constexpr bool operator!= (const CDrawMode& m) const { return modeIgnoringIntegralMode () != m.modeIgnoringIntegralMode (); }
private:
uint32_t mode;
};
//----------------------------
// @brief Text Alignment (Horizontal)
//----------------------------
enum CHoriTxtAlign
{
kLeftText = 0,
kCenterText,
kRightText
};
//----------------------------
// @brief Draw Style
//----------------------------
enum CDrawStyle
{
kDrawStroked = 0,
kDrawFilled,
kDrawFilledAndStroked
};
} // VSTGUI
@@ -0,0 +1,183 @@
// 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 "cdrawmethods.h"
#include "cbitmap.h"
#include "cstring.h"
#include "cdrawcontext.h"
#include "platform/iplatformfont.h"
namespace VSTGUI {
namespace CDrawMethods {
//------------------------------------------------------------------------
UTF8String createTruncatedText (TextTruncateMode mode, const UTF8String& text, CFontRef font,
CCoord maxWidth, const CPoint& textInset, uint32_t flags)
{
if (mode == kTextTruncateNone || text.length () < 2)
return text;
auto painter = font->getPlatformFont () ? font->getPlatformFont ()->getPainter () : nullptr;
if (!painter)
return text;
CCoord width = painter->getStringWidth (nullptr, text.getPlatformString (), true);
width += textInset.x * 2;
if (width > maxWidth)
{
std::string truncatedText;
UTF8String result;
auto left = text.begin ();
auto right = text.end ();
size_t distance = std::distance (left, right) - 1;
int32_t dir = 1;
while (distance > 0 && width != maxWidth)
{
distance = (distance / 2);
while ((dir > 0 ? (width > maxWidth) : (width < maxWidth)) && distance > 0)
{
if (mode == kTextTruncateHead)
{
if (dir > 0)
{
for (auto i = distance; i > 0 && left != right; --i)
++left;
}
else
{
for (auto i = distance; i > 0 && left != text.begin (); --i)
--left;
}
truncatedText = "..";
}
else if (mode == kTextTruncateTail)
{
if (dir > 0)
{
for (auto i = distance; i > 0 && left != right; --i)
--right;
}
else
{
for (auto i = distance; i > 0 && right != text.end (); --i)
++right;
}
truncatedText = "";
}
truncatedText += {left.base (), right.base ()};
if (mode == kTextTruncateTail)
truncatedText += "..";
result = truncatedText;
width = painter->getStringWidth (nullptr, result.getPlatformString (), true);
width += textInset.x * 2;
if (left == right)
break;
}
dir *= -1;
}
if (left == right && flags & kReturnEmptyIfTruncationIsPlaceholderOnly)
result = "";
return result;
}
return text;
}
//------------------------------------------------------------------------
void drawIconAndText (CDrawContext* context, CBitmap* iconToDraw, IconPosition iconPosition,
CHoriTxtAlign textAlignment, CCoord textIconMargin, CRect drawRect,
const UTF8String& title, CFontRef font, CColor textColor,
TextTruncateMode textTruncateMode)
{
if (iconToDraw)
{
CRect iconRect (0, 0, iconToDraw->getWidth (), iconToDraw->getHeight ());
iconRect.offset (drawRect.left, drawRect.top);
switch (iconPosition)
{
case kIconLeft:
{
iconRect.offset (textIconMargin,
drawRect.getHeight () / 2. - iconRect.getHeight () / 2.);
drawRect.left = iconRect.right;
drawRect.right -= textIconMargin;
if (textAlignment == kLeftText)
drawRect.left += textIconMargin;
break;
}
case kIconRight:
{
iconRect.offset (drawRect.getWidth () - (textIconMargin + iconRect.getWidth ()),
drawRect.getHeight () / 2. - iconRect.getHeight () / 2.);
drawRect.right = iconRect.left;
drawRect.left += textIconMargin;
if (textAlignment == kRightText)
drawRect.right -= textIconMargin;
break;
}
case kIconCenterAbove:
{
iconRect.offset (drawRect.getWidth () / 2. - iconRect.getWidth () / 2., 0);
if (title.empty ())
iconRect.offset (0, drawRect.getHeight () / 2. - iconRect.getHeight () / 2.);
else
{
iconRect.offset (0, drawRect.getHeight () / 2. -
(iconRect.getHeight () / 2. +
(textIconMargin + font->getSize ()) / 2.));
drawRect.top = iconRect.bottom + textIconMargin;
drawRect.setHeight (font->getSize ());
if (textAlignment == kLeftText)
drawRect.left += textIconMargin;
else if (textAlignment == kRightText)
drawRect.right -= textIconMargin;
}
break;
}
case kIconCenterBelow:
{
iconRect.offset (drawRect.getWidth () / 2. - iconRect.getWidth () / 2., 0);
if (title.empty ())
iconRect.offset (0, drawRect.getHeight () / 2. - iconRect.getHeight () / 2.);
else
{
iconRect.offset (0, drawRect.getHeight () / 2. - (iconRect.getHeight () / 2.) +
(textIconMargin + font->getSize ()) / 2.);
drawRect.top = iconRect.top - (textIconMargin + font->getSize ());
drawRect.setHeight (font->getSize ());
if (textAlignment == kLeftText)
drawRect.left += textIconMargin;
else if (textAlignment == kRightText)
drawRect.right -= textIconMargin;
}
break;
}
}
context->drawBitmap (iconToDraw, iconRect);
}
else
{
if (textAlignment == kLeftText)
drawRect.left += textIconMargin;
else if (textAlignment == kRightText)
drawRect.right -= textIconMargin;
}
if (!title.empty ())
{
context->setFont (font);
context->setFontColor (textColor);
if (textTruncateMode != kTextTruncateNone)
{
UTF8String truncatedText =
createTruncatedText (textTruncateMode, title, font, drawRect.getWidth (),
CPoint (0, 0), kReturnEmptyIfTruncationIsPlaceholderOnly);
context->drawString (truncatedText.getPlatformString (), drawRect, textAlignment);
}
else
context->drawString (title.getPlatformString (), drawRect, textAlignment);
}
}
}
} // namespaces
+74
View File
@@ -0,0 +1,74 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "cdrawdefs.h"
#include "cfont.h"
#include "cpoint.h"
namespace VSTGUI {
namespace CDrawMethods {
//-----------------------------------------------------------------------------
enum IconPosition : uint16_t {
/** icon left, text centered in the area next to the icon*/
kIconLeft = 0,
/** icon centered above the text, text centered */
kIconCenterAbove,
/** icon centered below the text, text centered */
kIconCenterBelow,
/** icon right, text centered in the area next to the icon */
kIconRight
};
//-----------------------------------------------------------------------------
enum TextTruncateMode : uint16_t {
kTextTruncateNone = 0,
kTextTruncateHead,
kTextTruncateTail
};
//-----------------------------------------------------------------------------
enum CreateTextTruncateFlags : uint16_t {
/** return an empty string if the truncated text is only the placeholder string */
kReturnEmptyIfTruncationIsPlaceholderOnly = 1 << 0,
};
//-----------------------------------------------------------------------------
/** create a truncated string
*
* @param mode truncation mode
* @param text text string
* @param font font
* @param maxWidth maximum width
* @param textInset text inset
* @param flags flags see CreateTextTruncateFlags
* @return truncated text or original text if no truncation needed
*/
UTF8String createTruncatedText (TextTruncateMode mode, const UTF8String& text, CFontRef font,
CCoord maxWidth, const CPoint& textInset = CPoint (0, 0),
uint32_t flags = 0);
//-----------------------------------------------------------------------------
/** draws an icon and a string into a rectangle
*
* @param context draw context
* @param iconToDraw icon to draw
* @param iconPosition position of the icon
* @param textAlignment alignment of the string
* @param textIconMargin margin of the string
* @param drawRect draw rectangle
* @param title string
* @param font font
* @param textColor font color
* @param truncateMode truncation mode
*/
void drawIconAndText (CDrawContext* context, CBitmap* iconToDraw, IconPosition iconPosition,
CHoriTxtAlign textAlignment, CCoord textIconMargin, CRect drawRect,
const UTF8String& title, CFontRef font, CColor textColor,
TextTruncateMode truncateMode = kTextTruncateNone);
}} // namespaces
@@ -0,0 +1,89 @@
// 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 "cdropsource.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
CDropSource::CDropEntry::CDropEntry (const void* inBuffer, uint32_t inBufferSize, Type inType)
: type (inType)
{
buffer.allocate (inBufferSize);
if (buffer.get ())
memcpy (buffer.get (), inBuffer, buffer.size ());
}
//-----------------------------------------------------------------------------
CDropSource::CDropEntry::CDropEntry (const CDropEntry& entry)
: type (entry.type)
{
buffer.allocate (entry.buffer.size ());
if (buffer.get ())
memcpy (buffer.get (), entry.buffer.get (), buffer.size ());
}
//-----------------------------------------------------------------------------
CDropSource::CDropEntry::CDropEntry (CDropEntry&& entry) noexcept
{
buffer = std::move (entry.buffer);
type = entry.type;
entry.type = kError;
}
//-----------------------------------------------------------------------------
CDropSource::CDropSource ()
{
}
//-----------------------------------------------------------------------------
CDropSource::CDropSource (const void* buffer, uint32_t bufferSize, Type type)
{
add (buffer, bufferSize, type);
}
//-----------------------------------------------------------------------------
bool CDropSource::add (const void* buffer, uint32_t bufferSize, Type type)
{
if (entries.size () == entries.max_size ())
return false;
entries.emplace_back (buffer, bufferSize, type);
return true;
}
//-----------------------------------------------------------------------------
uint32_t CDropSource::getCount () const
{
return static_cast<uint32_t> (entries.size ());
}
//-----------------------------------------------------------------------------
uint32_t CDropSource::getDataSize (uint32_t index) const
{
return index < getCount () ? static_cast<uint32_t> (entries[index].buffer.size ()) : 0;
}
//-----------------------------------------------------------------------------
CDropSource::Type CDropSource::getDataType (uint32_t index) const
{
return index < getCount () ? entries[index].type : kError;
}
//-----------------------------------------------------------------------------
uint32_t CDropSource::getData (uint32_t index, const void*& buffer, Type& type) const
{
if (index >= getCount ())
return 0;
buffer = entries[index].buffer.get ();
type = entries[index].type;
return static_cast<uint32_t> (entries[index].buffer.size ());
}
//-----------------------------------------------------------------------------
SharedPointer<IDataPackage> CDropSource::create (const void* buffer, uint32_t bufferSize, Type type)
{
return makeOwned<CDropSource> (buffer, bufferSize, type);
}
} // VSTGUI
+50
View File
@@ -0,0 +1,50 @@
// 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
#pragma once
#include "vstguibase.h"
#include "idatapackage.h"
#include "malloc.h"
#include <vector>
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CDropSource Declaration
//! @brief drop source
//!
//! @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class CDropSource : public IDataPackage
{
public:
static SharedPointer<IDataPackage> create (const void* buffer, uint32_t bufferSize, Type type);
CDropSource ();
CDropSource (const void* buffer, uint32_t bufferSize, Type type);
bool add (const void* buffer, uint32_t bufferSize, Type type);
// IDataPackage
uint32_t getCount () const final;
uint32_t getDataSize (uint32_t index) const final;
Type getDataType (uint32_t index) const final;
uint32_t getData (uint32_t index, const void*& buffer, Type& type) const final;
protected:
/// @cond ignore
struct CDropEntry {
Buffer<int8_t> buffer;
Type type;
CDropEntry (const void* buffer, uint32_t bufferSize, Type type);
CDropEntry (const CDropEntry& entry);
CDropEntry (CDropEntry&& entry) noexcept;
};
/// @endcond
using DropEntryVector = std::vector<CDropEntry>;
DropEntryVector entries;
};
} // VSTGUI
@@ -0,0 +1,314 @@
// 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 "cexternalview.h"
#include "cframe.h"
#include "platform/iplatformframe.h"
//------------------------------------------------------------------------
namespace VSTGUI {
//------------------------------------------------------------------------
struct CExternalViewBaseImpl
{
private:
using ExternalViewPtr = std::shared_ptr<ExternalView::IView>;
ExternalViewPtr view;
bool isAttached {false};
static ExternalView::IntRect fromCRect (const CRect& r)
{
ExternalView::IntRect res;
res.origin.x = static_cast<int64_t> (std::ceil (r.left));
res.origin.y = static_cast<int64_t> (std::ceil (r.top));
res.size.width = static_cast<int64_t> (std::floor (r.getWidth ()));
res.size.height = static_cast<int64_t> (std::floor (r.getHeight ()));
return res;
}
static CRect calculateSize (CViewContainer* parent, CRect newSize)
{
CFrame* frame = parent ? parent->getFrame () : nullptr;
while (parent && parent != frame)
{
CRect parentSize = parent->getViewSize ();
parent->getTransform ().transform (newSize);
newSize.offset (parentSize.left, parentSize.top);
newSize.bound (parentSize);
parent = static_cast<CViewContainer*> (parent->getParentView ());
}
if (frame)
frame->getTransform ().transform (newSize);
return newSize;
}
public:
CExternalViewBaseImpl (const ExternalViewPtr& v) : view (v) {}
void updateSize (CViewContainer* parent, CRect localSize, CRect globalSize)
{
if (!view)
return;
localSize = calculateSize (parent, localSize);
view->setViewSize (fromCRect (globalSize), fromCRect (localSize));
}
void attach (CFrame* frame)
{
if (!frame || !view)
return;
auto platformFrame = frame->getPlatformFrame ();
if (!platformFrame)
return;
ExternalView::PlatformViewType viewType = {};
switch (platformFrame->getPlatformType ())
{
case PlatformType::kHWND:
viewType = ExternalView::PlatformViewType::HWND;
break;
case PlatformType::kNSView:
viewType = ExternalView::PlatformViewType::NSView;
break;
default:
return;
}
if (!view->platformViewTypeSupported (viewType))
return;
isAttached = view->attach (platformFrame->getPlatformRepresentation (), viewType);
}
void remove ()
{
if (view && isAttached)
{
view->remove ();
isAttached = false;
}
}
void scaleFactorChanged (double scaleFactor)
{
if (view)
view->setContentScaleFactor (scaleFactor);
}
void takeFocus ()
{
if (view)
view->takeFocus ();
}
void looseFocus ()
{
if (view)
view->looseFocus ();
}
void enableMouse (bool state)
{
if (view)
view->setMouseEnabled (state);
}
ExternalView::IView* getView () const { return view.get (); }
};
//------------------------------------------------------------------------
struct CExternalView::Impl : CExternalViewBaseImpl
{
using CExternalViewBaseImpl::CExternalViewBaseImpl;
};
//------------------------------------------------------------------------
CExternalView::CExternalView (const CRect& r, const ExternalViewPtr& view) : CView (r)
{
impl = std::make_unique<Impl> (view);
impl->getView ()->setTookFocusCallback ([this] () {
if (auto frame = getFrame ())
frame->setFocusView (this);
});
}
//------------------------------------------------------------------------
CExternalView::~CExternalView () noexcept { impl->getView ()->setTookFocusCallback (nullptr); }
//------------------------------------------------------------------------
bool CExternalView::attached (CView* parent)
{
if (CView::attached (parent))
{
auto frame = parent->getFrame ();
impl->updateSize (parent->asViewContainer (), getViewSize (),
translateToGlobal (getViewSize ()));
impl->scaleFactorChanged (frame->getScaleFactor ());
impl->attach (frame);
frame->registerScaleFactorChangedListener (this);
return true;
}
return false;
}
//------------------------------------------------------------------------
bool CExternalView::removed (CView* parent)
{
if (auto frame = parent->getFrame ())
{
frame->unregisterScaleFactorChangedListener (this);
}
impl->remove ();
return CView::removed (parent);
}
//------------------------------------------------------------------------
void CExternalView::takeFocus () { impl->takeFocus (); }
//------------------------------------------------------------------------
void CExternalView::looseFocus () { impl->looseFocus (); }
//------------------------------------------------------------------------
void CExternalView::setViewSize (const CRect& rect, bool invalid)
{
CView::setViewSize (rect, invalid);
impl->updateSize (getParentView () ? getParentView ()->asViewContainer () : nullptr,
getViewSize (), translateToGlobal (getViewSize ()));
}
//------------------------------------------------------------------------
void CExternalView::parentSizeChanged ()
{
impl->updateSize (getParentView () ? getParentView ()->asViewContainer () : nullptr,
getViewSize (), translateToGlobal (getViewSize ()));
}
//------------------------------------------------------------------------
void CExternalView::onScaleFactorChanged (CFrame* frame, double newScaleFactor)
{
impl->scaleFactorChanged (newScaleFactor);
}
//------------------------------------------------------------------------
void CExternalView::setMouseEnabled (bool enable)
{
impl->enableMouse (enable);
CView::setMouseEnabled (enable);
}
//------------------------------------------------------------------------
ExternalView::IView* CExternalView::getExternalView () const { return impl->getView (); }
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
struct CExternalControl::Impl : CExternalViewBaseImpl
{
using CExternalViewBaseImpl::CExternalViewBaseImpl;
};
//------------------------------------------------------------------------
CExternalControl::CExternalControl (const CRect& r, const ExternalControlPtr& view)
: CControl (r, nullptr)
{
impl = std::make_unique<Impl> (view);
impl->getView ()->setTookFocusCallback ([this] () {
if (auto frame = getFrame ())
frame->setFocusView (this);
});
auto control = dynamic_cast<ExternalView::IControlViewExtension*> (impl->getView ());
vstgui_assert (
control, "Please provide an object that inherits from ExternalView::IControlViewExtension");
control->setEditCallbacks ({
[this] () { beginEdit (); },
[this] (double newValue) {
auto old = getValue ();
setValueNormalized (static_cast<float> (newValue));
if (old != getValue ())
valueChanged ();
},
[this] () { endEdit (); },
});
setWantsFocus (true);
}
//------------------------------------------------------------------------
CExternalControl::~CExternalControl () noexcept
{
impl->getView ()->setTookFocusCallback (nullptr);
auto control = dynamic_cast<ExternalView::IControlViewExtension*> (impl->getView ());
control->setEditCallbacks ({});
}
//------------------------------------------------------------------------
void CExternalControl::setValue (float val)
{
CControl::setValue (val);
auto control = dynamic_cast<ExternalView::IControlViewExtension*> (impl->getView ());
control->setValue (getValueNormalized ());
}
//------------------------------------------------------------------------
bool CExternalControl::attached (CView* parent)
{
if (CControl::attached (parent))
{
auto frame = parent->getFrame ();
impl->updateSize (parent->asViewContainer (), getViewSize (),
translateToGlobal (getViewSize ()));
impl->scaleFactorChanged (frame->getScaleFactor ());
impl->attach (frame);
frame->registerScaleFactorChangedListener (this);
return true;
}
return false;
}
//------------------------------------------------------------------------
bool CExternalControl::removed (CView* parent)
{
if (auto frame = parent->getFrame ())
{
frame->unregisterScaleFactorChangedListener (this);
}
impl->remove ();
return CControl::removed (parent);
}
//------------------------------------------------------------------------
void CExternalControl::takeFocus () { impl->takeFocus (); }
//------------------------------------------------------------------------
void CExternalControl::looseFocus () { impl->looseFocus (); }
//------------------------------------------------------------------------
void CExternalControl::setViewSize (const CRect& rect, bool invalid)
{
CControl::setViewSize (rect, invalid);
impl->updateSize (getParentView () ? getParentView ()->asViewContainer () : nullptr,
getViewSize (), translateToGlobal (getViewSize ()));
}
//------------------------------------------------------------------------
void CExternalControl::parentSizeChanged ()
{
impl->updateSize (getParentView () ? getParentView ()->asViewContainer () : nullptr,
getViewSize (), translateToGlobal (getViewSize ()));
}
//------------------------------------------------------------------------
void CExternalControl::onScaleFactorChanged (CFrame* frame, double newScaleFactor)
{
impl->scaleFactorChanged (newScaleFactor);
}
//------------------------------------------------------------------------
void CExternalControl::setMouseEnabled (bool enable)
{
impl->enableMouse (enable);
CControl::setMouseEnabled (enable);
}
//------------------------------------------------------------------------
ExternalView::IView* CExternalControl::getExternalView () const { return impl->getView (); }
//------------------------------------------------------------------------
bool CExternalControl::getFocusPath (CGraphicsPath& outPath) { return true; }
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,86 @@
// 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
#pragma once
#include "cview.h"
#include "controls/ccontrol.h"
#include "iscalefactorchangedlistener.h"
#include "iexternalview.h"
#include <memory>
//------------------------------------------------------------------------
namespace VSTGUI {
//------------------------------------------------------------------------
/** View to embed non CView views into VSTGUI
*
* This view is the umbrella for views from other view systems (like HWND child windows or
* NSViews).
* The actual implementation for the external view must be done via ExternalView::IView
*
* @ingroup new_in_4_13
*/
class CExternalView : public CView,
public IScaleFactorChangedListener,
public ExternalView::IViewEmbedder
{
public:
using ExternalViewPtr = std::shared_ptr<ExternalView::IView>;
CExternalView (const CRect& r, const ExternalViewPtr& view);
~CExternalView () noexcept;
bool attached (CView* parent) override;
bool removed (CView* parent) override;
void takeFocus () override;
void looseFocus () override;
void setViewSize (const CRect& rect, bool invalid = true) override;
void parentSizeChanged () override;
void onScaleFactorChanged (CFrame* frame, double newScaleFactor) override;
void setMouseEnabled (bool enable = true) override;
ExternalView::IView* getExternalView () const override;
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
//------------------------------------------------------------------------
class CExternalControl : public CControl,
public IScaleFactorChangedListener,
public ExternalView::IViewEmbedder
{
public:
using ExternalControlPtr = std::shared_ptr<ExternalView::IView>;
CExternalControl (const CRect& r, const ExternalControlPtr& control);
~CExternalControl () noexcept;
void setValue (float val) override;
bool attached (CView* parent) override;
bool removed (CView* parent) override;
void takeFocus () override;
void looseFocus () override;
void setViewSize (const CRect& rect, bool invalid = true) override;
void parentSizeChanged () override;
void onScaleFactorChanged (CFrame* frame, double newScaleFactor) override;
void setMouseEnabled (bool enable = true) override;
ExternalView::IView* getExternalView () const override;
CLASS_METHODS_NOCOPY (CExternalControl, CControl)
private:
void draw (CDrawContext* pContext) override {}
bool getFocusPath (CGraphicsPath& outPath) override;
struct Impl;
std::unique_ptr<Impl> impl;
};
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,319 @@
// 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 "platform/iplatformfileselector.h"
#include "platform/platformfactory.h"
#include "cfileselector.h"
#include "cframe.h"
#include "cstring.h"
#include <algorithm>
namespace VSTGUI {
//-----------------------------------------------------------------------------
struct CFileExtension::Impl : PlatformFileExtension
{
};
//-----------------------------------------------------------------------------
CFileExtension::CFileExtension ()
{
impl = std::make_unique<Impl> ();
}
//-----------------------------------------------------------------------------
CFileExtension::CFileExtension (const UTF8String& inDescription, const UTF8String& inExtension,
const UTF8String& inMimeType, int32_t inMacType,
const UTF8String& inUti)
: CFileExtension ()
{
init (inDescription, inExtension, inMimeType, inUti);
}
//-----------------------------------------------------------------------------
CFileExtension::CFileExtension (const CFileExtension& ext) : CFileExtension ()
{
*impl = *ext.impl;
}
//-----------------------------------------------------------------------------
CFileExtension::~CFileExtension () noexcept = default;
//-----------------------------------------------------------------------------
CFileExtension::CFileExtension (CFileExtension&& ext) noexcept
{
*this = std::move (ext);
}
//-----------------------------------------------------------------------------
CFileExtension& CFileExtension::operator= (CFileExtension&& ext) noexcept
{
std::swap (impl, ext.impl);
return *this;
}
//-----------------------------------------------------------------------------
void CFileExtension::init (const UTF8String& inDescription, const UTF8String& inExtension,
const UTF8String& inMimeType, const UTF8String& inUti)
{
impl->description = inDescription;
impl->extension = inExtension;
impl->mimeType = inMimeType;
impl->uti = inUti;
if (impl->description == nullptr && !impl->extension.empty ())
{
// TODO: query system for file type description
// Win32: AssocGetPerceivedType
// Mac: Uniform Type Identifier
}
}
//-----------------------------------------------------------------------------
bool CFileExtension::operator== (const CFileExtension& ext) const
{
bool result = false;
result = impl->extension == ext.impl->extension;
if (!result)
result = impl->mimeType == ext.impl->mimeType;
if (!result)
result = impl->uti == ext.impl->uti;
if (!result && impl->macType != 0 && ext.impl->macType != 0)
result = (impl->macType == ext.impl->macType);
return result;
}
//-----------------------------------------------------------------------------
const UTF8String& CFileExtension::getDescription () const
{
return impl->description;
}
//-----------------------------------------------------------------------------
const UTF8String& CFileExtension::getExtension () const
{
return impl->extension;
}
//-----------------------------------------------------------------------------
const UTF8String& CFileExtension::getMimeType () const
{
return impl->mimeType;
}
//-----------------------------------------------------------------------------
const UTF8String& CFileExtension::getUTI () const
{
return impl->uti;
}
//-----------------------------------------------------------------------------
int32_t CFileExtension::getMacType () const
{
return impl->macType;
}
//-----------------------------------------------------------------------------
CFileExtension::CFileExtension (const PlatformFileExtension& ext) : CFileExtension ()
{
impl->description = ext.description;
impl->extension = ext.extension;
impl->mimeType = ext.mimeType;
impl->uti = ext.uti;
impl->macType = ext.macType;
}
//-----------------------------------------------------------------------------
const PlatformFileExtension& CFileExtension::getPlatformFileExtension () const
{
return *impl;
}
//-----------------------------------------------------------------------------
const CFileExtension& CNewFileSelector::getAllFilesExtension ()
{
static CFileExtension allFilesExtension (PlatformAllFilesExtension);
return allFilesExtension;
}
//-----------------------------------------------------------------------------
IdStringPtr CNewFileSelector::kSelectEndMessage = "CNewFileSelector Select End Message";
//-----------------------------------------------------------------------------
// CNewFileSelector Implementation
//-----------------------------------------------------------------------------
struct CNewFileSelector::Impl : PlatformFileSelectorConfig
{
PlatformFileSelectorPtr platformFileSelector;
CFrame* frame {nullptr};
std::vector<UTF8String> result;
};
//-----------------------------------------------------------------------------
CNewFileSelector::CNewFileSelector (PlatformFileSelectorPtr&& platformFileSelector, CFrame* parent)
{
impl = std::make_unique<Impl> ();
impl->platformFileSelector = std::move (platformFileSelector);
impl->frame = parent;
}
//-----------------------------------------------------------------------------
CNewFileSelector::~CNewFileSelector () noexcept = default;
//-----------------------------------------------------------------------------
bool CNewFileSelector::run (CBaseObject* delegate)
{
if (delegate == nullptr)
{
#if DEBUG
DebugPrint ("You need to specify a delegate in CNewFileSelector::run (CBaseObject* delegate, void* parentWindow)\n");
#endif
return false;
}
if (impl->frame)
impl->frame->onStartLocalEventLoop ();
impl->doneCallback = [this, del = shared (delegate)] (std::vector<UTF8String>&& files) {
impl->result = std::move (files);
del->notify (this, CNewFileSelector::kSelectEndMessage);
};
setBit (impl->flags, PlatformFileSelectorFlags::RunModal, false);
return impl->platformFileSelector->run (*impl);
}
//-----------------------------------------------------------------------------
bool CNewFileSelector::run (CallbackFunc&& callback)
{
if (impl->frame)
impl->frame->onStartLocalEventLoop ();
remember ();
impl->doneCallback = [this,
cb = std::move (callback)] (std::vector<UTF8String>&& files) {
impl->result = std::move (files);
cb (this);
forget ();
};
setBit (impl->flags, PlatformFileSelectorFlags::RunModal, false);
return impl->platformFileSelector->run (*impl);
}
//-----------------------------------------------------------------------------
void CNewFileSelector::cancel ()
{
impl->platformFileSelector->cancel ();
}
//-----------------------------------------------------------------------------
bool CNewFileSelector::runModal ()
{
if (impl->frame)
impl->frame->onStartLocalEventLoop ();
setBit (impl->flags, PlatformFileSelectorFlags::RunModal, true);
impl->doneCallback = [&] (std::vector<UTF8String>&& files) {
impl->result = std::move (files);
};
return impl->platformFileSelector->run (*impl);
}
//-----------------------------------------------------------------------------
void CNewFileSelector::setTitle (const UTF8String& inTitle)
{
impl->title = inTitle;
}
//-----------------------------------------------------------------------------
void CNewFileSelector::setInitialDirectory (const UTF8String& path)
{
impl->initialPath = path;
}
//-----------------------------------------------------------------------------
void CNewFileSelector::setDefaultSaveName (const UTF8String& name)
{
impl->defaultSaveName = name;
}
//-----------------------------------------------------------------------------
void CNewFileSelector::setAllowMultiFileSelection (bool state)
{
setBit (impl->flags, PlatformFileSelectorFlags::MultiFileSelection, state);
}
//-----------------------------------------------------------------------------
void CNewFileSelector::setDefaultExtension (const CFileExtension& extension)
{
if (impl->defaultExtension != PlatformNoFileExtension)
{
#if DEBUG
DebugPrint ("VSTGUI Warning: It's not allowed to set a default extension twice on a "
"CFileSelector instance\n");
#endif
return;
}
auto it = std::find (impl->extensions.begin (), impl->extensions.end (),
extension.getPlatformFileExtension ());
if (it == impl->extensions.end ())
addFileExtension (extension);
impl->defaultExtension = extension.getPlatformFileExtension ();
}
//-----------------------------------------------------------------------------
void CNewFileSelector::addFileExtension (const CFileExtension& extension)
{
impl->extensions.emplace_back (extension.getPlatformFileExtension ());
}
//-----------------------------------------------------------------------------
void CNewFileSelector::addFileExtension (CFileExtension&& extension)
{
impl->extensions.emplace_back (std::move (extension.getPlatformFileExtension ()));
}
//-----------------------------------------------------------------------------
uint32_t CNewFileSelector::getNumSelectedFiles () const
{
return static_cast<uint32_t> (impl->result.size ());
}
//-----------------------------------------------------------------------------
UTF8StringPtr CNewFileSelector::getSelectedFile (uint32_t index) const
{
if (index < impl->result.size ())
return impl->result[index];
return nullptr;
}
//------------------------------------------------------------------------
CNewFileSelector* CNewFileSelector::create (CFrame* parent, Style style)
{
PlatformFileSelectorStyle platformStyle;
switch (style)
{
case Style::kSelectFile:
platformStyle = PlatformFileSelectorStyle::SelectFile;
break;
case Style::kSelectDirectory:
platformStyle = PlatformFileSelectorStyle::SelectDirectory;
break;
case Style::kSelectSaveFile:
platformStyle = PlatformFileSelectorStyle::SelectSaveFile;
break;
default:
vstgui_assert (false);
return nullptr;
}
if (auto platformSelector = getPlatformFactory ().createFileSelector (
platformStyle, parent ? parent->getPlatformFrame () : nullptr))
{
return new CNewFileSelector (std::move (platformSelector), parent);
}
return nullptr;
}
} // VSTGUI
+162
View File
@@ -0,0 +1,162 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "cstring.h"
#include <functional>
#include <memory>
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CFileExtension Declaration
//! @brief file extension description
//-----------------------------------------------------------------------------
class CFileExtension
{
public:
CFileExtension (const UTF8String& description, const UTF8String& extension,
const UTF8String& mimeType = "", int32_t macType = 0,
const UTF8String& uti = "");
CFileExtension (const CFileExtension& ext);
~CFileExtension () noexcept;
const UTF8String& getDescription () const;
const UTF8String& getExtension () const;
const UTF8String& getMimeType () const;
const UTF8String& getUTI () const;
int32_t getMacType () const;
bool operator== (const CFileExtension& ext) const;
//-----------------------------------------------------------------------------
CFileExtension (CFileExtension&& ext) noexcept;
CFileExtension& operator= (CFileExtension&& ext) noexcept;
CFileExtension (const PlatformFileExtension&);
const PlatformFileExtension& getPlatformFileExtension () const;
protected:
CFileExtension ();
void init (const UTF8String& description, const UTF8String& extension,
const UTF8String& mimeType, const UTF8String& uti);
struct Impl;
std::unique_ptr<Impl> impl;
};
//-----------------------------------------------------------------------------
// CNewFileSelector Declaration
//! @brief New file selector class
/*! @class CNewFileSelector
@section usage Usage
Running the file selector
@code
void MyClass::runFileSelector ()
{
CNewFileSelector* selector = CNewFileSelector::create (getFrame (), CNewFileSelector::kSelectFile);
if (selector)
{
selector->addFileExtension (CFileExtension ("AIFF", "aif", "audio/aiff"));
selector->setDefaultExtension (CFileExtension ("WAVE", "wav"));
selector->setTitle("Choose An Audio File");
selector->run (this);
selector->forget ();
}
}
@endcode
Getting results
@code
CMessageResult MyClass::notify (CBaseObject* sender, IdStringPtr message)
{
if (message == CNewFileSelector::kSelectEndMessage)
{
CNewFileSelector* sel = dynamic_cast<CNewFileSelector*>(sender);
if (sel)
{
// do anything with the selected files here
return kMessageNotified;
}
}
return parent::notify (sender, message);
}
@endcode
*/
//-----------------------------------------------------------------------------
class CNewFileSelector final : public CBaseObject
{
public:
enum Style {
/** select file(s) selector style */
kSelectFile,
/** select save file selector style */
kSelectSaveFile,
/** select directory style */
kSelectDirectory
};
//-----------------------------------------------------------------------------
/// @name CFileSelector running
//-----------------------------------------------------------------------------
//@{
/** create a new instance */
static CNewFileSelector* create (CFrame* parent = nullptr, Style style = kSelectFile);
CNewFileSelector (PlatformFileSelectorPtr&& platformFileSelector, CFrame* parent);
using CallbackFunc = std::function<void(CNewFileSelector*)>;
bool run (CallbackFunc&& callback);
/** the delegate will get a kSelectEndMessage throu the notify method where the sender is this CNewFileSelector object */
bool run (CBaseObject* delegate);
/** cancel running the file selector */
void cancel ();
/** run as modal dialog */
bool runModal ();
//@}
//-----------------------------------------------------------------------------
/// @name CFileSelector setup
//-----------------------------------------------------------------------------
//@{
/** set title of file selector */
void setTitle (const UTF8String& title);
/** set initial directory (UTF8 string) */
void setInitialDirectory (const UTF8String& path);
/** set initial save name (UTF8 string) */
void setDefaultSaveName (const UTF8String& name);
/** set default file extension */
void setDefaultExtension (const CFileExtension& extension);
/** set allow multi file selection (only valid for kSelectFile selector style) */
void setAllowMultiFileSelection (bool state);
/** add a file extension */
void addFileExtension (const CFileExtension& extension);
/** add a file extension */
void addFileExtension (CFileExtension&& extension);
//@}
//-----------------------------------------------------------------------------
/// @name CFileSelector result
//-----------------------------------------------------------------------------
//@{
/** get number of selected files */
uint32_t getNumSelectedFiles () const;
/** get selected file. Result is only valid as long as the instance of CNewFileSelector is valid. */
UTF8StringPtr getSelectedFile (uint32_t index) const;
//@}
/** get the all files extension */
static const CFileExtension& getAllFilesExtension ();
static IdStringPtr kSelectEndMessage;
//-----------------------------------------------------------------------------
CLASS_METHODS_NOCOPY (CNewFileSelector, CBaseObject)
protected:
~CNewFileSelector () noexcept override;
struct Impl;
std::unique_ptr<Impl> impl;
};
} // VSTGUI
+221
View File
@@ -0,0 +1,221 @@
// 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 "cfont.h"
#include "cstring.h"
#include "platform/platformfactory.h"
#include "platform/iplatformfont.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// Global Fonts
//-----------------------------------------------------------------------------
struct GlobalFonts
{
SharedPointer<CFontDesc> systemFont;
SharedPointer<CFontDesc> normalFontVeryBig;
SharedPointer<CFontDesc> normalFontBig;
SharedPointer<CFontDesc> normalFont;
SharedPointer<CFontDesc> normalFontSmall;
SharedPointer<CFontDesc> normalFontSmaller;
SharedPointer<CFontDesc> normalFontVerySmall;
SharedPointer<CFontDesc> symbolFont;
};
static GlobalFonts globalFonts;
//-----------------------------------------------------------------------------
CFontRef kSystemFont = nullptr;
CFontRef kNormalFontVeryBig = nullptr;
CFontRef kNormalFontBig = nullptr;
CFontRef kNormalFont = nullptr;
CFontRef kNormalFontSmall = nullptr;
CFontRef kNormalFontSmaller = nullptr;
CFontRef kNormalFontVerySmall = nullptr;
CFontRef kSymbolFont = nullptr;
//-----------------------------------------------------------------------------
void CFontDesc::init ()
{
#if MAC
#if TARGET_OS_IPHONE
globalFonts.systemFont = makeOwned<CFontDesc> ("Helvetica", 12);
globalFonts.normalFontVeryBig = makeOwned<CFontDesc> ("ArialMT", 18);
globalFonts.normalFontBig = makeOwned<CFontDesc> ("ArialMT", 14);
globalFonts.normalFont = makeOwned<CFontDesc> ("ArialMT", 12);
globalFonts.normalFontSmall = makeOwned<CFontDesc> ("ArialMT", 11);
globalFonts.normalFontSmaller = makeOwned<CFontDesc> ("ArialMT", 10);
globalFonts.normalFontVerySmall = makeOwned<CFontDesc> ("ArialMT", 9);
globalFonts.symbolFont = makeOwned<CFontDesc> ("Symbol", 12);
#else
globalFonts.systemFont = makeOwned<CFontDesc> ("Lucida Grande", 12);
globalFonts.normalFontVeryBig = makeOwned<CFontDesc> ("Arial", 18);
globalFonts.normalFontBig = makeOwned<CFontDesc> ("Arial", 14);
globalFonts.normalFont = makeOwned<CFontDesc> ("Arial", 12);
globalFonts.normalFontSmall = makeOwned<CFontDesc> ("Arial", 11);
globalFonts.normalFontSmaller = makeOwned<CFontDesc> ("Arial", 10);
globalFonts.normalFontVerySmall = makeOwned<CFontDesc> ("Arial", 9);
globalFonts.symbolFont = makeOwned<CFontDesc> ("Symbol", 12);
#endif
#elif WINDOWS
globalFonts.systemFont = makeOwned<CFontDesc> ("Arial", 12);
globalFonts.normalFontVeryBig = makeOwned<CFontDesc> ("Arial", 18);
globalFonts.normalFontBig = makeOwned<CFontDesc> ("Arial", 14);
globalFonts.normalFont = makeOwned<CFontDesc> ("Arial", 12);
globalFonts.normalFontSmall = makeOwned<CFontDesc> ("Arial", 11);
globalFonts.normalFontSmaller = makeOwned<CFontDesc> ("Arial", 10);
globalFonts.normalFontVerySmall = makeOwned<CFontDesc> ("Arial", 9);
globalFonts.symbolFont = makeOwned<CFontDesc> ("Symbol", 13);
#else
globalFonts.systemFont = makeOwned<CFontDesc> ("Arial", 12);
globalFonts.normalFontVeryBig = makeOwned<CFontDesc> ("Arial", 18);
globalFonts.normalFontBig = makeOwned<CFontDesc> ("Arial", 14);
globalFonts.normalFont = makeOwned<CFontDesc> ("Arial", 12);
globalFonts.normalFontSmall = makeOwned<CFontDesc> ("Arial", 11);
globalFonts.normalFontSmaller = makeOwned<CFontDesc> ("Arial", 10);
globalFonts.normalFontVerySmall = makeOwned<CFontDesc> ("Arial", 9);
globalFonts.symbolFont = makeOwned<CFontDesc> ("Symbol", 13);
#endif
kSystemFont = globalFonts.systemFont;
kNormalFontVeryBig = globalFonts.normalFontVeryBig;
kNormalFontBig = globalFonts.normalFontBig;
kNormalFont = globalFonts.normalFont;
kNormalFontSmall = globalFonts.normalFontSmall;
kNormalFontSmaller = globalFonts.normalFontSmaller;
kNormalFontVerySmall = globalFonts.normalFontVerySmall;
kSymbolFont = globalFonts.symbolFont;
}
//-----------------------------------------------------------------------------
void CFontDesc::cleanup ()
{
globalFonts.systemFont = nullptr;
globalFonts.normalFontVeryBig = nullptr;
globalFonts.normalFontBig = nullptr;
globalFonts.normalFont = nullptr;
globalFonts.normalFontSmall = nullptr;
globalFonts.normalFontSmaller = nullptr;
globalFonts.normalFontVerySmall = nullptr;
globalFonts.symbolFont = nullptr;
kSystemFont = nullptr;
kNormalFontVeryBig = nullptr;
kNormalFontBig = nullptr;
kNormalFont = nullptr;
kNormalFontSmall = nullptr;
kNormalFontSmaller = nullptr;
kNormalFontVerySmall = nullptr;
kSymbolFont = nullptr;
}
//-----------------------------------------------------------------------------
// CFontDesc Implementation
/*! @class CFontDesc
The CFontDesc class replaces the old font handling. You have now the possibilty to use whatever font you like
as long as it is available on the system. You should cache your own CFontDesc as this speeds up drawing on some systems.
\note New in 4.9: It's now possible to use custom fonts. Fonts must reside inside the Bundle/Package at PackageRoot/Resources/Fonts/.
*/
//-----------------------------------------------------------------------------
CFontDesc::CFontDesc (const UTF8String& inName, const CCoord& inSize, const int32_t inStyle)
: size (inSize)
, style (inStyle)
, platformFont (nullptr)
{
setName (inName);
}
//-----------------------------------------------------------------------------
CFontDesc::CFontDesc (const CFontDesc& font)
: size (0)
, style (0)
, platformFont (nullptr)
{
*this = font;
}
//------------------------------------------------------------------------
CFontDesc::~CFontDesc () noexcept
{
vstgui_assert (getNbReference () == 0, "Always use shared pointers with CFontDesc!");
}
//-----------------------------------------------------------------------------
void CFontDesc::beforeDelete ()
{
freePlatformFont ();
}
//-----------------------------------------------------------------------------
auto CFontDesc::getPlatformFont () const -> const PlatformFontPtr
{
if (platformFont == nullptr)
platformFont = getPlatformFactory ().createFont (name, size, style);
return platformFont;
}
//-----------------------------------------------------------------------------
const IFontPainter* CFontDesc::getFontPainter () const
{
IPlatformFont* pf = getPlatformFont ();
if (pf)
return pf->getPainter ();
return nullptr;
}
//-----------------------------------------------------------------------------
void CFontDesc::freePlatformFont ()
{
platformFont = nullptr;
}
//-----------------------------------------------------------------------------
void CFontDesc::setName (const UTF8String& newName)
{
if (name == newName)
return;
name = newName;
freePlatformFont ();
}
//-----------------------------------------------------------------------------
void CFontDesc::setSize (CCoord newSize)
{
size = newSize;
freePlatformFont ();
}
//-----------------------------------------------------------------------------
void CFontDesc::setStyle (int32_t newStyle)
{
style = newStyle;
freePlatformFont ();
}
//-----------------------------------------------------------------------------
CFontDesc& CFontDesc::operator = (const CFontDesc& f)
{
setName (f.getName ());
setSize (f.getSize ());
setStyle (f.getStyle ());
return *this;
}
//-----------------------------------------------------------------------------
bool CFontDesc::operator == (const CFontDesc& f) const
{
if (size != f.getSize ())
return false;
if (style != f.getStyle ())
return false;
if (name != f.getName ())
return false;
return true;
}
} // VSTGUI
+76
View File
@@ -0,0 +1,76 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "cstring.h"
#include <string>
#include <list>
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CFontDesc Declaration
//! @brief font class
//-----------------------------------------------------------------------------
class CFontDesc : public AtomicReferenceCounted
{
public:
CFontDesc (const UTF8String& name = "", const CCoord& size = 0, const int32_t style = 0);
CFontDesc (const CFontDesc& font);
~CFontDesc () noexcept override;
//-----------------------------------------------------------------------------
/// @name Size, Name and Style Methods
//-----------------------------------------------------------------------------
//@{
/** get the name of the font */
const UTF8String& getName () const { return name; }
/** get the height of the font */
const CCoord& getSize () const { return size; }
/** get the style of the font */
const int32_t& getStyle () const { return style; }
/** set the name of the font */
virtual void setName (const UTF8String& newName);
/** set the height of the font */
virtual void setSize (CCoord newSize);
/** set the style of the font @sa CTxtFace */
virtual void setStyle (int32_t newStyle);
//@}
virtual const PlatformFontPtr getPlatformFont () const;
virtual const IFontPainter* getFontPainter () const;
virtual CFontDesc& operator= (const CFontDesc&);
virtual bool operator== (const CFontDesc&) const;
virtual bool operator!= (const CFontDesc& other) const { return !(*this == other);}
static void init ();
static void cleanup ();
protected:
void beforeDelete () override;
virtual void freePlatformFont ();
UTF8String name;
CCoord size;
int32_t style;
mutable PlatformFontPtr platformFont;
};
//-----------------------------------------------------------------------------
// Global fonts
//-----------------------------------------------------------------------------
extern CFontRef kSystemFont;
extern CFontRef kNormalFontVeryBig;
extern CFontRef kNormalFontBig;
extern CFontRef kNormalFont;
extern CFontRef kNormalFontSmall;
extern CFontRef kNormalFontSmaller;
extern CFontRef kNormalFontVerySmall;
extern CFontRef kSymbolFont;
} // VSTGUI
File diff suppressed because it is too large Load Diff
+395
View File
@@ -0,0 +1,395 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "cviewcontainer.h"
#include "optional.h"
#include "platform/iplatformframecallback.h"
namespace VSTGUI {
//----------------------------
// @brief Knob Mode
//----------------------------
enum CKnobMode
{
kCircularMode = 0,
kRelativCircularMode,
kLinearMode
};
/** Message send to all parents of the new focus view */
extern IdStringPtr kMsgNewFocusView;
/** Message send to all parents of the old focus view */
extern IdStringPtr kMsgOldFocusView;
//-----------------------------------------------------------------------------
// CFrame Declaration
//! @brief The CFrame is the parent container of all views
/// @ingroup containerviews
//-----------------------------------------------------------------------------
class CFrame final : public CViewContainer, public IPlatformFrameCallback
{
public:
CFrame (const CRect& size, VSTGUIEditorInterface* pEditor);
//-----------------------------------------------------------------------------
/// @name CFrame Methods
//-----------------------------------------------------------------------------
//@{
bool open (void* pSystemWindow, PlatformType systemWindowType = PlatformType::kDefaultNative, IPlatformFrameConfig* = nullptr);
/** closes the frame and calls forget */
void close ();
/** set zoom factor */
bool setZoom (double zoomFactor);
/** get zoom factor */
double getZoom () const;
void setBitmapInterpolationQuality (BitmapInterpolationQuality quality); ///< set interpolation quality for bitmaps
BitmapInterpolationQuality getBitmapInterpolationQuality () const; ///< get interpolation quality for bitmaps
double getScaleFactor () const;
void idle ();
/** get the current time (in ms) */
uint64_t getTicks () const;
/** default knob mode if host does not provide one */
static int32_t kDefaultKnobMode;
/** get hosts knob mode */
int32_t getKnobMode () const;
bool setPosition (CCoord x, CCoord y);
bool getPosition (CCoord& x, CCoord& y) const;
bool setSize (CCoord width, CCoord height);
bool getSize (CRect* pSize) const;
bool getSize (CRect& pSize) const;
CPoint checkSizeConstraint (const CPoint& newSize) const;
VSTGUI_DEPRECATED (
/** set a modal view. deprecated use beginModalViewSession instead */
bool setModalView (CView* pView);)
/** get the currently active modal view or nullptr if there is none */
CView* getModalView () const;
/** begin a new modal view session
*
* A modal view session is active until endModalViewSession is called and in that time all UI
* events are only dispatched to the modal view or its child views.
* Modal view sessions can be stacked but must be ended in the same order.
*
* @param view new modal view (ownership is transfered to frame, the same as addView)
* @return a unique session identifier
*/
Optional<ModalViewSessionID> beginModalViewSession (CView* view);
/** end a modal view session
*
* @param session a session identifer
* @return true on success
*/
bool endModalViewSession (ModalViewSessionID session);
void beginEdit (int32_t index);
void endEdit (int32_t index);
/** get current mouse location */
bool getCurrentMouseLocation (CPoint& where) const;
/** get current mouse buttons and key modifiers */
CButtonState getCurrentMouseButtons () const;
/** set mouse cursor */
void setCursor (CCursorType type);
void setFocusView (CView* pView);
CView* getFocusView () const;
bool advanceNextFocusView (CView* oldFocus, bool reverse = false) override;
void onViewAdded (CView* pView);
void onViewRemoved (CView* pView);
/** called when the platform view/window is activated/deactivated */
void onActivate (bool state);
void invalidate (const CRect& rect);
/** scroll src rect by distance */
void scrollRect (const CRect& src, const CPoint& distance);
/** enable or disable tooltips */
void enableTooltips (bool state, uint32_t delayTimeInMs = 1000);
/** get animator for this frame */
Animation::Animator* getAnimator ();
/** get the clipboard data. data is owned by the caller */
SharedPointer<IDataPackage> getClipboard ();
/** set the clipboard data. */
void setClipboard (const SharedPointer<IDataPackage>& data);
IViewAddedRemovedObserver* getViewAddedRemovedObserver () const;
void setViewAddedRemovedObserver (IViewAddedRemovedObserver* observer);
/** register a keyboard hook */
void registerKeyboardHook (IKeyboardHook* hook);
/** unregister a keyboard hook */
void unregisterKeyboardHook (IKeyboardHook* hook);
/** register a mouse observer */
void registerMouseObserver (IMouseObserver* observer);
/** unregister a mouse observer */
void unregisterMouseObserver (IMouseObserver* observer);
VSTGUI_DEPRECATED_MSG (
void registerScaleFactorChangedListeneer (IScaleFactorChangedListener* listener) {
registerScaleFactorChangedListener (listener);
},
"use registerScaleFactorChangedListener")
VSTGUI_DEPRECATED_MSG (
void unregisterScaleFactorChangedListeneer (IScaleFactorChangedListener* listener) {
unregisterScaleFactorChangedListener (listener);
},
"use unregisterScaleFactorChangedListener")
void registerScaleFactorChangedListener (IScaleFactorChangedListener* listener);
void unregisterScaleFactorChangedListener (IScaleFactorChangedListener* listener);
void registerFocusViewObserver (IFocusViewObserver* observer);
void unregisterFocusViewObserver (IFocusViewObserver* observer);
//@}
//-----------------------------------------------------------------------------
/// @name Focus Drawing Methods [new in 4.0]
//! If focus drawing is enabled, the focus view will get a focus ring around it defined with the focus width and the focus color.
//! Views can define their own shape with the IFocusDrawing interface.
//-----------------------------------------------------------------------------
//@{
/** enable focus drawing */
void setFocusDrawingEnabled (bool state);
/** is focus drawing enabled */
bool focusDrawingEnabled () const;
/** set focus draw color */
void setFocusColor (const CColor& color);
/** get focus draw color */
CColor getFocusColor () const;
/** set focus draw width */
void setFocusWidth (CCoord width);
/** get focus draw width */
CCoord getFocusWidth () const;
//@}
using EventProcessingFunction = std::function<void ()>;
/** Queue a function which will be executed after the current event was handled.
* Only allowed when inEventProcessing () is true
*
* @param func Function to execute
* @return true if the function was added to the execution queue
*/
bool doAfterEventProcessing (EventProcessingFunction&& func);
/** Queue a function which will be executed after the current event was handled.
* Only allowed when inEventProcessing () is true
*
* @param func Function to execute
* @return true if the function was added to the execution queue
*/
bool doAfterEventProcessing (const EventProcessingFunction& func);
/** Returns true if an event is currently being processed. */
bool inEventProcessing () const;
void onStartLocalEventLoop ();
bool performDrag (const DragDescription& desc, const SharedPointer<IDragCallback>& callback);
void invalid () override { invalidRect (getViewSize ()); setDirty (false); }
void invalidRect (const CRect& rect) override;
bool removeView (CView* pView, bool withForget = true) override;
bool removeAll (bool withForget = true) override;
CView* getViewAt (const CPoint& where, const GetViewOptions& options = GetViewOptions ()) const override;
CViewContainer* getContainerAt (const CPoint& where, const GetViewOptions& options = GetViewOptions ().deep ()) const override;
bool getViewsAt (const CPoint& where, ViewList& views, const GetViewOptions& options = GetViewOptions ().deep ()) const override;
bool hitTestSubViews (const CPoint& where, const Event& event) override;
CPoint& frameToLocal (CPoint& point) const override { return point; }
CPoint& localToFrame (CPoint& point) const override { return point; }
// CView
bool attached (CView* parent) override;
void draw (CDrawContext* pContext) override;
void drawRect (CDrawContext* pContext, const CRect& updateRect) override;
void setViewSize (const CRect& rect, bool invalid = true) override;
void dispatchEvent (Event& event) override;
VSTGUIEditorInterface* getEditor () const override;
IPlatformFrame* getPlatformFrame () const;
#if DEBUG
void dumpHierarchy () override;
#endif
CLASS_METHODS_NOCOPY(CFrame, CViewContainer)
//-------------------------------------------
protected:
struct CollectInvalidRects;
CFrame (const CFrame&) = delete;
~CFrame () noexcept override = default;
void beforeDelete () override;
void checkMouseViews (const MouseEvent& event);
void clearMouseViews (const CPoint& where, Modifiers modifiers, bool callMouseExit = true);
void removeFromMouseViews (CView* view);
void setCollectInvalidRects (CollectInvalidRects* collectInvalidRects);
// keyboard hooks
void dispatchKeyboardEventToHooks (KeyboardEvent& event);
// mouse observers
void callMouseObserverMouseEntered (CView* view);
void callMouseObserverMouseExited (CView* view);
void callMouseObserverOtherMouseEvent (MouseEvent& event);
void dispatchNewScaleFactor (double newScaleFactor);
// platform frame
void platformDrawRects (const PlatformGraphicsDeviceContextPtr& context, double scaleFactor,
const std::vector<CRect>& rects) override;
void platformOnEvent (Event& event) override;
DragOperation platformOnDragEnter (DragEventData data) override;
DragOperation platformOnDragMove (DragEventData data) override;
void platformOnDragLeave (DragEventData data) override;
bool platformOnDrop (DragEventData data) override;
void platformOnActivate (bool state) override;
void platformOnWindowActivate (bool state) override;
void platformScaleFactorChanged (double newScaleFactor) override;
#if VSTGUI_TOUCH_EVENT_HANDLING
void platformOnTouchEvent (ITouchEvent& event) override;
#endif
private:
#if VSTGUI_ENABLE_DEPRECATED_METHODS
void endLegacyModalViewSession ();
#endif
void initModalViewSession (const ModalViewSession& session);
void clearModalViewSessions ();
void dispatchKeyboardEvent (KeyboardEvent& event);
void dispatchMouseEvent (MouseEvent& event);
void dispatchMouseDownEvent (MouseDownEvent& event);
void dispatchMouseMoveEvent (MouseMoveEvent& event);
void dispatchMouseUpEvent (MouseUpEvent& event);
void dispatchEvent (CView* view, Event& event);
void dispatchEventToChildren (Event& event);
struct Impl;
Impl* pImpl {nullptr};
};
//----------------------------------------------------
class VSTGUIEditorInterface
{
public:
virtual void doIdleStuff () {}
virtual int32_t getKnobMode () const { return -1; }
virtual void beginEdit (int32_t index) {}
virtual void endEdit (int32_t index) {}
/** frame will change size, if this returns false the upstream implementation does not allow it and thus the size of the frame will not change */
virtual bool beforeSizeChange (const CRect& newSize, const CRect& oldSize) { return true; }
virtual CFrame* getFrame () const { return frame; }
protected:
VSTGUIEditorInterface () = default;
virtual ~VSTGUIEditorInterface () noexcept = default;
CFrame* frame {nullptr};
};
//-----------------------------------------------------------------------------
// IMouseObserver Declaration
//! @brief generic mouse observer interface for CFrame
//-----------------------------------------------------------------------------
class IMouseObserver
{
public:
virtual ~IMouseObserver() noexcept = default;
virtual void onMouseEntered (CView* view, CFrame* frame) = 0;
virtual void onMouseExited (CView* view, CFrame* frame) = 0;
virtual void onMouseEvent (MouseEvent& event, CFrame* frame) = 0;
};
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//-----------------------------------------------------------------------------
class OldMouseObserverAdapter : public IMouseObserver
{
public:
void onMouseEntered (CView* view, CFrame* frame) override {}
void onMouseExited (CView* view, CFrame* frame) override {}
void onMouseEvent (MouseEvent& event, CFrame* frame) override;
virtual CMouseEventResult onMouseMoved (CFrame* frame, const CPoint& where, const CButtonState& buttons);
virtual CMouseEventResult onMouseDown (CFrame* frame, const CPoint& where, const CButtonState& buttons);
};
#endif
//-----------------------------------------------------------------------------
// IKeyboardHook Declaration
//! @brief generic keyboard hook interface for CFrame
//! @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class IKeyboardHook
{
public:
virtual ~IKeyboardHook () noexcept = default;
/** the event will not be dispatched further if it is consumed. */
virtual void onKeyboardEvent (KeyboardEvent& event, CFrame* frame) = 0;
};
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
class OldKeyboardHookAdapter : public IKeyboardHook
{
public:
virtual int32_t onKeyDown (const VstKeyCode& code, CFrame* frame) = 0;
virtual int32_t onKeyUp (const VstKeyCode& code, CFrame* frame) = 0;
private:
void onKeyboardEvent (KeyboardEvent& event, CFrame* frame) override;
};
#endif
//-----------------------------------------------------------------------------
// IViewAddedRemovedObserver Declaration
//! @brief view added removed observer interface for CFrame
//! @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class IViewAddedRemovedObserver
{
public:
virtual ~IViewAddedRemovedObserver () noexcept = default;
virtual void onViewAdded (CFrame* frame, CView* view) = 0;
virtual void onViewRemoved (CFrame* frame, CView* view) = 0;
};
//-----------------------------------------------------------------------------
// IFocusViewObserver Declaration
//! @brief focus view observer interface for CFrame
//! @ingroup new_in_4_5
//-----------------------------------------------------------------------------
class IFocusViewObserver
{
public:
virtual ~IFocusViewObserver () noexcept = default;
virtual void onFocusViewChanged (CFrame* frame, CView* newFocusView, CView* oldFocusView) = 0;
};
} // VSTGUI
+65
View File
@@ -0,0 +1,65 @@
// 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 "cgradient.h"
#include "platform/iplatformgradient.h"
#include "platform/platformfactory.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
CGradient* CGradient::create (const GradientColorStopMap& colorStopMap)
{
if (auto pg = getPlatformFactory ().createGradient ())
{
pg->setColorStops (colorStopMap);
return new CGradient (std::move (pg));
}
return nullptr;
}
//-----------------------------------------------------------------------------
CGradient* CGradient::create (double color1Start, double color2Start, const CColor& color1,
const CColor& color2)
{
GradientColorStopMap map;
map.emplace (color1Start, color1);
map.emplace (color2Start, color2);
return create (map);
}
//-----------------------------------------------------------------------------
CGradient::CGradient (PlatformGradientPtr&& platformGradient)
: platformGradient (std::move (platformGradient))
{
}
//-----------------------------------------------------------------------------
CGradient::~CGradient () noexcept = default;
//-----------------------------------------------------------------------------
void CGradient::addColorStop (double start, const CColor& color)
{
addColorStop (std::make_pair (start, color));
}
//-----------------------------------------------------------------------------
void CGradient::addColorStop (const GradientColorStop& colorStop)
{
platformGradient->addColorStop (colorStop);
}
//-----------------------------------------------------------------------------
const GradientColorStopMap& CGradient::getColorStops () const
{
return platformGradient->getColorStops ();
}
//-----------------------------------------------------------------------------
const PlatformGradientPtr& CGradient::getPlatformGradient () const
{
return platformGradient;
}
} // VSTGUI
+43
View File
@@ -0,0 +1,43 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "ccolor.h"
#include <map>
#include <algorithm>
namespace VSTGUI {
//-----------------------------------------------------------------------------
/// @brief Gradient Object [new in 4.0]
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class CGradient : public AtomicReferenceCounted
{
public:
static CGradient* create (const GradientColorStopMap& colorStopMap);
static CGradient* create (double color1Start, double color2Start, const CColor& color1, const CColor& color2);
CGradient (PlatformGradientPtr&& platformGradient);
~CGradient () noexcept override;
//-----------------------------------------------------------------------------
/// @name Member Access
//-----------------------------------------------------------------------------
//@{
void addColorStop (double start, const CColor& color);
void addColorStop (const GradientColorStop& colorStop);
const GradientColorStopMap& getColorStops () const;
//@}
const PlatformGradientPtr& getPlatformGradient () const;
protected:
PlatformGradientPtr platformGradient;
};
} // VSTGUI
@@ -0,0 +1,171 @@
// 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 "cgradientview.h"
#include "cdrawcontext.h"
#include "cgraphicspath.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
CGradientView::CGradientView (const CRect& size)
: CView (size)
{
}
//------------------------------------------------------------------------
void CGradientView::attributeChanged ()
{
invalid ();
}
//-----------------------------------------------------------------------------
void CGradientView::setGradientStyle (GradientStyle style)
{
if (gradientStyle != style)
{
gradientStyle = style;
attributeChanged ();
}
}
//------------------------------------------------------------------------
void CGradientView::setGradient (CGradient* newGradient)
{
if (gradient != newGradient)
{
gradient = newGradient;
attributeChanged ();
}
}
//-----------------------------------------------------------------------------
void CGradientView::setFrameColor (const CColor& newColor)
{
if (newColor != frameColor)
{
frameColor = newColor;
attributeChanged ();
}
}
//-----------------------------------------------------------------------------
void CGradientView::setGradientAngle (double angle)
{
if (angle != gradientAngle)
{
gradientAngle = angle;
attributeChanged ();
}
}
//-----------------------------------------------------------------------------
void CGradientView::setRoundRectRadius (CCoord radius)
{
if (radius != roundRectRadius)
{
roundRectRadius = radius;
path = nullptr;
attributeChanged ();
}
}
//-----------------------------------------------------------------------------
void CGradientView::setFrameWidth (CCoord width)
{
if (width != frameWidth)
{
frameWidth = width;
path = nullptr;
attributeChanged ();
}
}
//-----------------------------------------------------------------------------
void CGradientView::setDrawAntialiased (bool state)
{
if (state != drawAntialiased)
{
drawAntialiased = state;
attributeChanged ();
}
}
//-----------------------------------------------------------------------------
void CGradientView::setRadialCenter (const CPoint& center)
{
if (radialCenter != center)
{
radialCenter = center;
attributeChanged ();
}
}
//-----------------------------------------------------------------------------
void CGradientView::setRadialRadius (CCoord radius)
{
if (radialRadius != radius)
{
radialRadius = radius;
attributeChanged ();
}
}
//-----------------------------------------------------------------------------
void CGradientView::setViewSize (const CRect& rect, bool invalid)
{
if (rect != getViewSize ())
{
CView::setViewSize (rect, invalid);
path = nullptr;
}
}
//-----------------------------------------------------------------------------
void CGradientView::draw (CDrawContext* context)
{
auto lineWidth = getFrameWidth ();
if (lineWidth < 0.)
lineWidth = context->getHairlineSize ();
if (path == nullptr)
{
CRect r = getViewSize ();
r.inset (lineWidth / 2., lineWidth / 2.);
path = owned (context->createRoundRectGraphicsPath (r, roundRectRadius));
}
if (path && gradient)
{
context->setDrawMode (drawAntialiased ? kAntiAliasing : kAliasing);
if (gradientStyle == kLinearGradient)
{
CPoint colorStartPoint (0, 0);
colorStartPoint.x = getViewSize ().left + getViewSize ().getWidth () / 2 + cos (radians (gradientAngle-90)) * getViewSize ().getWidth () / 2;
colorStartPoint.y = getViewSize ().top + getViewSize ().getHeight () / 2 + sin (radians (gradientAngle-90)) * getViewSize ().getHeight () / 2;
CPoint colorEndPoint (0, getViewSize ().getHeight ());
colorEndPoint.x = getViewSize ().left + getViewSize ().getWidth () / 2 + cos (radians (gradientAngle+90)) * getViewSize ().getWidth () / 2;
colorEndPoint.y = getViewSize ().top + getViewSize ().getHeight () / 2 + sin (radians (gradientAngle+90)) * getViewSize ().getHeight () / 2;
context->fillLinearGradient (path, *gradient, colorStartPoint, colorEndPoint, false);
}
else
{
CPoint center (radialCenter);
center.x *= getViewSize ().getWidth ();
center.y *= getViewSize ().getHeight ();
center.offset (getViewSize ().left, getViewSize ().top);
context->fillRadialGradient (path, *gradient, center, radialRadius * std::max (getViewSize ().getWidth (), getViewSize ().getHeight ()));
}
if (frameColor.alpha != 0 && lineWidth > 0.)
{
context->setDrawMode (drawAntialiased ? kAntiAliasing : kAliasing);
context->setFrameColor (frameColor);
context->setLineWidth (lineWidth);
context->setLineStyle (kLineSolid);
context->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
}
}
} // VSTGUI
@@ -0,0 +1,73 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "cview.h"
#include "ccolor.h"
#include "cgradient.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
/// @brief View which draws a gradient
/// @ingroup new_in_4_2
//-----------------------------------------------------------------------------
class CGradientView : public CView
{
public:
explicit CGradientView (const CRect& size);
~CGradientView () noexcept override = default;
//-----------------------------------------------------------------------------
/// @name Gradient Style Methods
//-----------------------------------------------------------------------------
//@{
enum GradientStyle {
kLinearGradient,
kRadialGradient
};
void setGradientStyle (GradientStyle style);
void setGradient (CGradient* gradient);
void setFrameColor (const CColor& newColor);
void setGradientAngle (double angle);
void setRoundRectRadius (CCoord radius);
void setFrameWidth (CCoord width);
void setDrawAntialiased (bool state);
void setRadialCenter (const CPoint& center);
void setRadialRadius (CCoord radius);
GradientStyle getGradientStyle () const { return gradientStyle; }
CGradient* getGradient () const { return gradient; }
const CColor& getFrameColor () const { return frameColor; }
double getGradientAngle () const { return gradientAngle; }
CCoord getRoundRectRadius () const { return roundRectRadius; }
CCoord getFrameWidth () const { return frameWidth; }
bool getDrawAntialised () const { return drawAntialiased; }
const CPoint& getRadialCenter () const { return radialCenter; }
CCoord getRadialRadius () const { return radialRadius; }
//@}
// override
void setViewSize (const CRect& rect, bool invalid = true) override;
void draw (CDrawContext* context) override;
protected:
virtual void attributeChanged ();
GradientStyle gradientStyle {kLinearGradient};
CColor frameColor {kBlackCColor};
double gradientAngle {0.};
CCoord roundRectRadius {5.};
CCoord frameWidth {1.};
CCoord radialRadius {1.};
CPoint radialCenter {0.5, 0.5};
bool drawAntialiased {true};
SharedPointer<CGraphicsPath> path;
SharedPointer<CGradient> gradient;
};
} // VSTGUI
@@ -0,0 +1,330 @@
// 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 "cdrawcontext.h"
#include "cgradient.h"
#include "cgraphicspath.h"
#include "cgraphicstransform.h"
#include "platform/iplatformgraphicspath.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
void CGraphicsPath::addRoundRect (const CRect& size, CCoord radius)
{
if (radius <= 0.)
{
addRect (size);
return;
}
CRect rect2 (size);
rect2.normalize ();
const CCoord left = rect2.left;
const CCoord right = rect2.right;
const CCoord top = rect2.top;
const CCoord bottom = rect2.bottom;
beginSubpath (CPoint (right - radius, top));
addArc (CRect (right - 2.0 * radius, top, right, top + 2.0 * radius), 270., 360., true);
addArc (CRect (right - 2.0 * radius, bottom - 2.0 * radius, right, bottom), 0., 90., true);
addArc (CRect (left, bottom - 2.0 * radius, left + 2.0 * radius, bottom), 90., 180., true);
addArc (CRect (left, top, left + 2.0 * radius, top + 2.0 * radius), 180., 270., true);
closeSubpath ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addPath (const CGraphicsPath& inPath, CGraphicsTransform* transformation)
{
for (auto e : inPath.elements)
{
if (transformation)
{
switch (e.type)
{
case Element::kArc:
{
transformation->transform (
e.instruction.arc.rect.left, e.instruction.arc.rect.right,
e.instruction.arc.rect.top, e.instruction.arc.rect.bottom);
break;
}
case Element::kEllipse:
case Element::kRect:
{
transformation->transform (e.instruction.rect.left, e.instruction.rect.right,
e.instruction.rect.top, e.instruction.rect.bottom);
break;
}
case Element::kBeginSubpath:
case Element::kLine:
{
transformation->transform (e.instruction.point.x, e.instruction.point.y);
break;
}
case Element::kBezierCurve:
{
transformation->transform (e.instruction.curve.control1.x,
e.instruction.curve.control1.y);
transformation->transform (e.instruction.curve.control2.x,
e.instruction.curve.control2.y);
transformation->transform (e.instruction.curve.end.x,
e.instruction.curve.end.y);
break;
}
case Element::kCloseSubpath:
{
break;
}
}
}
elements.emplace_back (e);
}
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise)
{
Element e;
e.type = Element::kArc;
CRect2Rect (rect, e.instruction.arc.rect);
e.instruction.arc.startAngle = startAngle;
e.instruction.arc.endAngle = endAngle;
e.instruction.arc.clockwise = clockwise;
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addEllipse (const CRect& rect)
{
Element e;
e.type = Element::kEllipse;
CRect2Rect (rect, e.instruction.rect);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addRect (const CRect& rect)
{
Element e;
e.type = Element::kRect;
CRect2Rect (rect, e.instruction.rect);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addLine (const CPoint& to)
{
Element e;
e.type = Element::kLine;
CPoint2Point (to, e.instruction.point);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addBezierCurve (const CPoint& control1, const CPoint& control2,
const CPoint& end)
{
Element e;
e.type = Element::kBezierCurve;
CPoint2Point (control1, e.instruction.curve.control1);
CPoint2Point (control2, e.instruction.curve.control2);
CPoint2Point (end, e.instruction.curve.end);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::beginSubpath (const CPoint& start)
{
Element e;
e.type = Element::kBeginSubpath;
CPoint2Point (start, e.instruction.point);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::closeSubpath ()
{
Element e;
e.type = Element::kCloseSubpath;
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
CGraphicsPath::CGraphicsPath (const PlatformGraphicsPathFactoryPtr& factory,
PlatformGraphicsPathPtr&& path)
: factory (factory), path (std::move (path))
{
}
//------------------------------------------------------------------------
CGraphicsPath::CGraphicsPath (const CGraphicsPath& p) : elements (p.elements), factory (p.factory)
{
}
//-----------------------------------------------------------------------------
CGraphicsPath::~CGraphicsPath () noexcept {}
//-----------------------------------------------------------------------------
CGradient* CGraphicsPath::createGradient (double color1Start, double color2Start,
const CColor& color1, const CColor& color2)
{
return CGradient::create (color1Start, color2Start, color1, color2);
}
//-----------------------------------------------------------------------------
void CGraphicsPath::makePlatformGraphicsPath (PlatformGraphicsPathFillMode fillMode)
{
if (!factory)
return;
path = factory->createPath (fillMode);
if (!path)
return;
for (const auto& e : elements)
{
switch (e.type)
{
case Element::kArc:
{
path->addArc (rect2CRect (e.instruction.arc.rect), e.instruction.arc.startAngle,
e.instruction.arc.endAngle, e.instruction.arc.clockwise);
break;
}
case Element::kEllipse:
{
path->addEllipse (rect2CRect (e.instruction.rect));
break;
}
case Element::kRect:
{
path->addRect (rect2CRect (e.instruction.rect));
break;
}
case Element::kLine:
{
path->addLine (point2CPoint (e.instruction.point));
break;
}
case Element::kBezierCurve:
{
path->addBezierCurve (point2CPoint (e.instruction.curve.control1),
point2CPoint (e.instruction.curve.control2),
point2CPoint (e.instruction.curve.end));
break;
}
case Element::kBeginSubpath:
{
path->beginSubpath (point2CPoint (e.instruction.point));
break;
}
case Element::kCloseSubpath:
{
path->closeSubpath ();
break;
}
}
}
path->finishBuilding ();
}
//-----------------------------------------------------------------------------
bool CGraphicsPath::ensurePlatformGraphicsPathValid (PlatformGraphicsPathFillMode fillMode)
{
if (path == nullptr || (path->getFillMode () != PlatformGraphicsPathFillMode::Ignored &&
path->getFillMode () != fillMode))
{
makePlatformGraphicsPath (fillMode);
}
return path != nullptr;
}
//-----------------------------------------------------------------------------
void CGraphicsPath::dirty ()
{
path = nullptr;
}
//-----------------------------------------------------------------------------
bool CGraphicsPath::hitTest (const CPoint& p, bool evenOddFilled, CGraphicsTransform* transform)
{
ensurePlatformGraphicsPathValid (evenOddFilled ? PlatformGraphicsPathFillMode::Alternate
: PlatformGraphicsPathFillMode::Winding);
return path ? path->hitTest (p, evenOddFilled, transform) : false;
}
//-----------------------------------------------------------------------------
CPoint CGraphicsPath::getCurrentPosition ()
{
CPoint res;
if (!elements.empty ())
{
const auto& e = elements.back ();
switch (e.type)
{
case Element::kBeginSubpath:
{
res = point2CPoint (e.instruction.point);
break;
}
case Element::kCloseSubpath:
{
// TODO: find opening point
break;
}
case Element::kArc:
{
// TODO: calculate end point
break;
}
case Element::kEllipse:
{
res = {e.instruction.rect.left +
(e.instruction.rect.right - e.instruction.rect.left) / 2.,
e.instruction.rect.bottom};
break;
}
case Element::kRect:
{
res = rect2CRect (e.instruction.rect).getTopLeft ();
break;
}
case Element::kLine:
{
res = point2CPoint (e.instruction.point);
break;
}
case Element::kBezierCurve:
{
res = point2CPoint (e.instruction.curve.end);
break;
}
}
}
return res;
}
//-----------------------------------------------------------------------------
CRect CGraphicsPath::getBoundingBox ()
{
ensurePlatformGraphicsPathValid (path ? path->getFillMode ()
: PlatformGraphicsPathFillMode::Winding);
return path ? path->getBoundingBox () : CRect ();
}
//-----------------------------------------------------------------------------
const PlatformGraphicsPathPtr&
CGraphicsPath::getPlatformPath (PlatformGraphicsPathFillMode fillMode)
{
ensurePlatformGraphicsPathValid (fillMode);
return path;
}
} // VSTGUI
+167
View File
@@ -0,0 +1,167 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "ccolor.h"
#include "crect.h"
#include <vector>
namespace VSTGUI {
//-----------------------------------------------------------------------------
/// @brief Graphics Path Object
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class CGraphicsPath : public AtomicReferenceCounted
{
public:
//-----------------------------------------------------------------------------
/// @name Creating gradients
//-----------------------------------------------------------------------------
//@{
/**
* @brief creates a new gradient object, you must release it with forget() when you're done with it
* @param color1Start value between zero and one which defines the normalized start offset for color1
* @param color2Start value between zero and one which defines the normalized start offset for color2
* @param color1 the first color of the gradient
* @param color2 the second color of the gradient
* @return a new gradient object
*/
CGradient* createGradient (double color1Start, double color2Start, const CColor& color1,
const CColor& color2);
//@}
//-----------------------------------------------------------------------------
/// @name Adding Elements
//-----------------------------------------------------------------------------
//@{
/** add an arc to the path. Begins a new subpath if no elements were added before. */
void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise);
/** add an ellipse to the path. Begins a new subpath if no elements were added before. */
void addEllipse (const CRect& rect);
/** add a rectangle to the path. Begins a new subpath if no elements were added before. */
void addRect (const CRect& rect);
/** add another path to the path. Begins a new subpath if no elements were added before. */
void addPath (const CGraphicsPath& path, CGraphicsTransform* transformation = nullptr);
/** add a line to the path. A subpath must begin before */
void addLine (const CPoint& to);
/** add a bezier curve to the path. A subpath must begin before */
void addBezierCurve (const CPoint& control1, const CPoint& control2, const CPoint& end);
/** begin a new subpath. */
void beginSubpath (const CPoint& start);
/** close a subpath. A straight line will be added from the current point to the start point. */
void closeSubpath ();
inline void beginSubpath (CCoord x, CCoord y)
{
beginSubpath (CPoint (x, y));
}
inline void addLine (CCoord x, CCoord y)
{
addLine (CPoint(x, y));
}
inline void addBezierCurve (CCoord cp1x, CCoord cp1y, CCoord cp2x, CCoord cp2y, CCoord x, CCoord y)
{
addBezierCurve (CPoint (cp1x, cp1y), CPoint (cp2x, cp2y), CPoint (x, y));
}
//@}
//-----------------------------------------------------------------------------
/// @name Helpers
//-----------------------------------------------------------------------------
//@{
void addRoundRect (const CRect& size, CCoord radius);
//@}
//-----------------------------------------------------------------------------
/// @name Hit Testing
//-----------------------------------------------------------------------------
//@{
bool hitTest (const CPoint& p, bool evenOddFilled = false,
CGraphicsTransform* transform = nullptr);
//@}
//-----------------------------------------------------------------------------
/// @name States
//-----------------------------------------------------------------------------
//@{
CPoint getCurrentPosition ();
CRect getBoundingBox ();
//@}
CGraphicsPath (const PlatformGraphicsPathFactoryPtr& factory, PlatformGraphicsPathPtr&& path);
CGraphicsPath (const CGraphicsPath& p);
~CGraphicsPath () noexcept override;
const PlatformGraphicsPathPtr& getPlatformPath (PlatformGraphicsPathFillMode fillMode);
protected:
void makePlatformGraphicsPath (PlatformGraphicsPathFillMode fillMode);
bool ensurePlatformGraphicsPathValid (PlatformGraphicsPathFillMode fillMode);
void dirty ();
/// @cond ignore
struct Rect {
CCoord left;
CCoord top;
CCoord right;
CCoord bottom;
};
struct Point {
CCoord x;
CCoord y;
};
struct Arc {
Rect rect;
double startAngle;
double endAngle;
bool clockwise;
};
struct BezierCurve {
Point control1;
Point control2;
Point end;
};
struct Element {
enum Type {
kArc = 0,
kEllipse,
kRect,
kLine,
kBezierCurve,
kBeginSubpath,
kCloseSubpath
};
Type type;
union Instruction {
Arc arc;
Rect rect;
BezierCurve curve;
Point point;
} instruction;
};
static void CRect2Rect (const CRect& rect, CGraphicsPath::Rect& r) {r.left = rect.left;r.right = rect.right;r.top = rect.top;r.bottom = rect.bottom;}
static void CPoint2Point (const CPoint& point, CGraphicsPath::Point& p) {p.x = point.x;p.y = point.y;}
static CRect rect2CRect (const Rect& r) { return CRect (r.left, r.top, r.right, r.bottom); }
static CPoint point2CPoint (const Point& p) { return CPoint (p.x, p.y); }
/// @endcond
using ElementList = std::vector<Element>;
ElementList elements;
PlatformGraphicsPathFactoryPtr factory;
PlatformGraphicsPathPtr path;
};
} // VSTGUI
@@ -0,0 +1,154 @@
// 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
#pragma once
#include "cpoint.h"
#include "crect.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif
namespace VSTGUI {
static inline double radians (double degrees) { return degrees * M_PI / 180; }
//-----------------------------------------------------------------------------
/// @brief Graphics Transform Matrix
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
struct CGraphicsTransform
{
double m11 {1.};
double m12 {0.};
double m21 {0.};
double m22 {1.};
double dx {0.};
double dy {0.};
CGraphicsTransform () = default;
CGraphicsTransform (double _m11, double _m12, double _m21, double _m22, double _dx, double _dy)
: m11 (_m11), m12 (_m12), m21 (_m21), m22 (_m22), dx (_dx), dy (_dy)
{}
CGraphicsTransform& translate (double x, double y)
{
*this = CGraphicsTransform (1, 0, 0, 1, x, y) * this;
return *this;
}
CGraphicsTransform& translate (const CPoint& p)
{
return translate (p.x, p.y);
}
CGraphicsTransform& scale (double x, double y)
{
*this = CGraphicsTransform (x, 0., 0., y, 0., 0.) * this;
return *this;
}
CGraphicsTransform& scale (const CPoint& p)
{
return scale (p.x, p.y);
}
CGraphicsTransform& rotate (double angle)
{
angle = radians (angle);
*this = CGraphicsTransform (cos (angle), -sin (angle), sin (angle), cos (angle), 0, 0) * this;
return *this;
}
CGraphicsTransform& rotate (double angle, const CPoint& center)
{
return translate (-center.x, -center.y).rotate (angle).translate (center.x, center.y);
}
CGraphicsTransform& skewX (double angle)
{
*this = CGraphicsTransform (1, std::tan (radians (angle)), 0, 1, 0, 0) * *this;
return *this;
}
CGraphicsTransform& skewY (double angle)
{
*this = CGraphicsTransform (1, 0, std::tan (radians (angle)), 1, 0, 0) * *this;
return *this;
}
bool isInvariant () const
{
return *this == CGraphicsTransform ();
}
void transform (CCoord& x, CCoord& y) const
{
CCoord x2 = m11*x + m12*y + dx;
CCoord y2 = m21*x + m22*y + dy;
x = x2;
y = y2;
}
void transform (CCoord& left, CCoord& right, CCoord& top, CCoord& bottom) const
{
transform (left, top);
transform (right, bottom);
}
CPoint& transform (CPoint& p) const
{
transform (p.x, p.y);
return p;
}
CRect& transform (CRect& r) const
{
transform (r.left, r.right, r.top, r.bottom);
return r;
}
CGraphicsTransform inverse () const
{
CGraphicsTransform result;
const double denominator = m11 * m22 - m12 * m21;
if (denominator != 0)
{
result.m11 = m22 / denominator;
result.m12 = -m12 / denominator;
result.m21 = -m21 / denominator;
result.m22 = m11 / denominator;
result.dx = ((m12 * dy) - (m22 * dx)) / denominator;
result.dy = ((m21 * dx) - (m11 * dy)) / denominator;
}
return result;
}
CGraphicsTransform operator* (const CGraphicsTransform& t) const
{
CGraphicsTransform result;
result.m11 = (m11 * t.m11) + (m12 * t.m21);
result.m21 = (m21 * t.m11) + (m22 * t.m21);
result.dx = (m11 * t.dx) + (m12 * t.dy) + dx;
result.m12 = (m11 * t.m12) + (m12 * t.m22);
result.m22 = (m21 * t.m12) + (m22 * t.m22);
result.dy = (m21 * t.dx) + (m22 * t.dy) + dy;
return result;
}
CGraphicsTransform operator* (const CGraphicsTransform* t) const { return *this * *t; }
bool operator== (const CGraphicsTransform& t) const
{
return m11 == t.m11 && m12 == t.m12 && m21 == t.m21 && m22 == t.m22 && dx == t.dx && dy == t.dy;
}
bool operator!= (const CGraphicsTransform& t) const
{
return m11 != t.m11 || m12 != t.m12 || m21 != t.m21 || m22 != t.m22 || dx != t.dx || dy != t.dy;
}
};
} // VSTGUI
@@ -0,0 +1,111 @@
// 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
#pragma once
#include "crect.h"
#include <vector>
namespace VSTGUI {
//-----------------------------------------------------------------------------
struct CInvalidRectList
{
using RectList = std::vector<CRect>;
bool add (const CRect& r);
RectList::iterator begin () { return list.begin (); }
RectList::iterator end () { return list.end (); }
RectList::const_iterator begin () const { return list.begin (); }
RectList::const_iterator end () const { return list.end (); }
void erase (RectList::iterator it) { list.erase (it); }
void clear () { list.clear (); }
const RectList& data () const { return list; }
bool empty () const { return list.empty (); }
private:
RectList list;
};
//-----------------------------------------------------------------------------
inline bool CInvalidRectList::add (const CRect& r)
{
for (auto it = list.begin (), end = list.end (); it != end; ++it)
{
// the same rectangle is already in the list
if (*it == r)
return false;
// the new rectangle is part of one already in the list
if (it->rectInside (r))
return false;
// if the new rectangle contains one of the previous rectangles
if (r.rectInside (*it))
{
list.erase (it);
return add (r);
}
// now check if the combined rect has the same or less area as both rects together
auto area1 = r.getWidth () * r.getHeight ();
auto area2 = it->getWidth () * it->getHeight ();
CRect jr (*it);
jr.unite (r);
auto joinedArea = jr.getWidth () * jr.getHeight ();
if (joinedArea <= (area1 + area2))
{
list.erase (it);
return add (jr);
}
}
list.emplace_back (r);
return true;
}
//-----------------------------------------------------------------------------
inline void joinNearbyInvalidRects (CInvalidRectList& list, CCoord maxDistance)
{
for (auto it = list.begin (); it != list.end (); ++it)
{
for (auto it2 = list.begin (); it2 != list.end (); ++it2)
{
if (it2 == it)
continue;
if (it->left == it2->left && it->right == it2->right)
{
CCoord distance;
if (it->bottom < it2->top)
distance = it2->top - it->bottom;
else
distance = it->top - it2->bottom;
if (distance <= maxDistance)
{
it->unite (*it2);
list.erase (it2);
joinNearbyInvalidRects (list, maxDistance);
return;
}
}
if (it->top == it2->top && it->bottom == it2->bottom)
{
CCoord distance;
if (it->right < it2->left)
distance = it2->left - it->right;
else
distance = it->left - it2->right;
if (distance <= maxDistance)
{
it->unite (*it2);
list.erase (it2);
joinNearbyInvalidRects (list, maxDistance);
return;
}
}
}
}
}
//-----------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,260 @@
// 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 "clayeredviewcontainer.h"
#include "cframe.h"
#include "cdrawcontext.h"
#include "coffscreencontext.h"
#include "platform/iplatformframe.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
CLayeredViewContainer::CLayeredViewContainer (const CRect& r)
: CViewContainer (r)
{
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::setZIndex (uint32_t _zIndex)
{
if (_zIndex != zIndex)
{
zIndex = _zIndex;
if (layer)
layer->setZIndex (zIndex);
}
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::updateLayerSize ()
{
if (!layer)
return;
CRect newSize = getViewSize ();
getTransform ().transform (newSize);
auto frame = getFrame ();
auto* parent = static_cast<CViewContainer*> (getParentView ());
while (parent && parent != frame)
{
CRect parentSize = parent->getViewSize ();
parent->getTransform ().transform (newSize);
newSize.offset (parentSize.left, parentSize.top);
newSize.bound (parentSize);
parent = static_cast<CViewContainer*> (parent->getParentView ());
}
frame->getTransform ().transform (newSize);
if (parentLayerView)
{
CPoint p (parentLayerView->getVisibleViewSize ().getTopLeft ());
parentLayerView->translateToGlobal (p);
newSize.offsetInverse (p);
}
if (layer)
layer->setSize (newSize);
}
//-----------------------------------------------------------------------------
bool CLayeredViewContainer::removed (CView* parent)
{
if (!isAttached ())
return false;
registerListeners (false);
if (layer)
{
layer = nullptr;
parentLayerView = nullptr;
getFrame ()->unregisterScaleFactorChangedListener (this);
}
return CViewContainer::removed (parent);
}
//-----------------------------------------------------------------------------
bool CLayeredViewContainer::attached (CView* parent)
{
if (isAttached ())
return false;
setParentView (parent);
setParentFrame (parent->getFrame ());
if (auto frame = getFrame ())
{
while (parent && dynamic_cast<CFrame*>(parent) == nullptr)
{
parentLayerView = dynamic_cast<CLayeredViewContainer*>(parent);
if (parentLayerView)
{
break;
}
parent = parent->getParentView ();
}
layer = frame->getPlatformFrame ()->createPlatformViewLayer (this, parentLayerView ? parentLayerView->layer : nullptr);
if (layer)
{
layer->setZIndex (zIndex);
layer->setAlpha (getAlphaValue ());
updateLayerSize ();
frame->registerScaleFactorChangedListener (this);
}
}
parent = getParentView ();
registerListeners (true);
setParentView (nullptr);
setParentFrame (nullptr);
return CViewContainer::attached (parent);
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::registerListeners (bool state)
{
auto* parent = static_cast<CViewContainer*> (getParentView ());
while (parent)
{
if (state)
parent->registerViewContainerListener (this);
else
parent->unregisterViewContainerListener (this);
parent = static_cast<CViewContainer*> (parent->getParentView ());
}
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::viewContainerTransformChanged (CViewContainer* container)
{
updateLayerSize ();
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::invalid ()
{
CRect r = getViewSize ();
r.originize ();
invalidRect (r);
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::invalidRect (const CRect& rect)
{
if (layer)
{
CRect r (rect);
getDrawTransform ().transform (r);
layer->invalidRect (r);
}
else
{
CViewContainer::invalidRect (rect);
}
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::parentSizeChanged ()
{
CViewContainer::parentSizeChanged ();
if (layer)
{
updateLayerSize ();
invalid ();
}
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::setViewSize (const CRect& rect, bool invalid)
{
CViewContainer::setViewSize (rect, invalid);
updateLayerSize ();
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::setAlphaValue (float alpha)
{
if (layer)
{
setAlphaValueNoInvalidate (alpha);
layer->setAlpha (alpha);
}
else
CViewContainer::setAlphaValue (alpha);
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::drawRect (CDrawContext* pContext, const CRect& updateRect)
{
auto drawsIntoBitmap = false;
if (auto offscreenContext = dynamic_cast<COffscreenContext*> (pContext))
drawsIntoBitmap = offscreenContext->getBitmap () != nullptr;
if (!layer || drawsIntoBitmap)
CViewContainer::drawRect (pContext, updateRect);
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::drawViewLayerRects (const PlatformGraphicsDeviceContextPtr& context,
double scaleFactor, const std::vector<CRect>& rects)
{
CGraphicsTransform drawTransform = getDrawTransform ();
CRect visibleSize = getVisibleViewSize ();
CRect viewSize = getViewSize ();
CPoint p (viewSize.left < 0 ? viewSize.left - visibleSize.left : visibleSize.left,
viewSize.top < 0 ? viewSize.top - visibleSize.top : visibleSize.top);
auto surfaceSize = getViewSize ();
surfaceSize.originize ();
CDrawContext drawContext (context, surfaceSize, scaleFactor);
CDrawContext::Transform transform (
drawContext, drawTransform * CGraphicsTransform ().translate (-p.x, -p.y));
for (auto dirtyRect : rects)
{
drawTransform.inverse ().transform (dirtyRect);
dirtyRect.offset (p.x, p.y);
drawContext.saveGlobalState ();
drawContext.setClipRect (dirtyRect);
CViewContainer::drawRect (&drawContext, dirtyRect);
drawContext.restoreGlobalState ();
}
}
//-----------------------------------------------------------------------------
CGraphicsTransform CLayeredViewContainer::getDrawTransform () const
{
using ParentViews = std::list<CViewContainer*>;
CGraphicsTransform transform;
ParentViews parents;
auto frame = getFrame ();
auto* parent = static_cast<CViewContainer*> (getParentView ());
while (parent && parent != frame)
{
parents.push_front (parent);
parent = static_cast<CViewContainer*> (parent->getParentView ());
}
for (const auto& p : parents)
transform = p->getTransform () * transform;
auto self = static_cast<const CViewContainer*> (this);
if (self)
transform = self->getTransform () * transform;
if (frame)
transform = frame->getTransform () * transform;
return transform;
}
//-----------------------------------------------------------------------------
void CLayeredViewContainer::onScaleFactorChanged (CFrame* frame, double newScaleFactor)
{
if (layer)
layer->onScaleFactorChanged (newScaleFactor);
}
}
@@ -0,0 +1,59 @@
// 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
#pragma once
#include "cviewcontainer.h"
#include "iviewlistener.h"
#include "iscalefactorchangedlistener.h"
#include "platform/iplatformviewlayer.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CLayeredViewContainer Declaration
//! @brief a view container which draws into a platform layer on top of a parent layer or the platform view
//! @ingroup containerviews
//! @ingroup new_in_4_2
//! A CLayeredViewContainer creates a platform layer on top of a parent layer or the platform view of CFrame
//! if available on that platform and draws into it, otherwise it acts exactly like a CViewContainer
//-----------------------------------------------------------------------------
class CLayeredViewContainer : public CViewContainer,
public IPlatformViewLayerDelegate,
public ViewContainerListenerAdapter,
public IScaleFactorChangedListener
{
public:
explicit CLayeredViewContainer (const CRect& r = CRect (0, 0, 0, 0));
~CLayeredViewContainer () noexcept override = default;
IPlatformViewLayer* getPlatformLayer () const { return layer; }
void setZIndex (uint32_t zIndex);
uint32_t getZIndex () const { return zIndex; }
bool removed (CView* parent) override;
bool attached (CView* parent) override;
void invalid () override;
void invalidRect (const CRect& rect) override;
void parentSizeChanged () override;
void setViewSize (const CRect& rect, bool invalid = true) override;
void setAlphaValue (float alpha) override;
//-----------------------------------------------------------------------------
protected:
void drawRect (CDrawContext* pContext, const CRect& updateRect) override;
void drawViewLayerRects (const PlatformGraphicsDeviceContextPtr& context, double scaleFactor,
const std::vector<CRect>& rects) override;
void viewContainerTransformChanged (CViewContainer* container) override;
void onScaleFactorChanged (CFrame* frame, double newScaleFactor) override;
void updateLayerSize ();
CGraphicsTransform getDrawTransform () const;
void registerListeners (bool state);
SharedPointer<IPlatformViewLayer> layer;
CLayeredViewContainer* parentLayerView {nullptr};
uint32_t zIndex {0};
};
} // VSTGUI
+91
View File
@@ -0,0 +1,91 @@
// 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 "clinestyle.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CLineStyle::CLineStyle (LineCap _cap, LineJoin _join, CCoord _dashPhase, uint32_t _dashCount, const CCoord* _dashLengths)
: cap (_cap)
, join (_join)
, dashPhase (_dashPhase)
{
if (_dashCount && _dashLengths)
{
for (uint32_t i = 0; i < _dashCount; i++)
dashLengths.emplace_back (_dashLengths[i]);
}
}
//-----------------------------------------------------------------------------
CLineStyle::CLineStyle (LineCap _cap, LineJoin _join, CCoord _dashPhase, const CoordVector& _dashLengths)
: cap (_cap)
, join (_join)
, dashPhase (_dashPhase)
, dashLengths (_dashLengths)
{
}
//-----------------------------------------------------------------------------
CLineStyle::CLineStyle (const CLineStyle& lineStyle)
{
*this = lineStyle;
}
//-----------------------------------------------------------------------------
CLineStyle::CLineStyle (LineCap _cap, LineJoin _join, CCoord _dashPhase, CoordVector&& _dashLengths) noexcept
: cap (_cap)
, join (_join)
, dashPhase (_dashPhase)
, dashLengths (std::move (_dashLengths))
{
}
//-----------------------------------------------------------------------------
CLineStyle::CLineStyle (CLineStyle&& cls) noexcept
{
*this = std::move (cls);
}
//-----------------------------------------------------------------------------
CLineStyle& CLineStyle::operator= (CLineStyle&& cls) noexcept
{
dashLengths.clear ();
cap = cls.cap;
join = cls.join;
dashPhase = cls.dashPhase;
dashLengths = std::move (cls.dashLengths);
return *this;
}
//-----------------------------------------------------------------------------
bool CLineStyle::operator== (const CLineStyle& cls) const
{
if (cap == cls.cap && join == cls.join && dashPhase == cls.dashPhase && dashLengths == cls.dashLengths)
{
return true;
}
return false;
}
//-----------------------------------------------------------------------------
CLineStyle& CLineStyle::operator= (const CLineStyle& cls)
{
dashLengths.clear ();
cap = cls.cap;
join = cls.join;
dashPhase = cls.dashPhase;
dashLengths = cls.dashLengths;
return *this;
}
//-----------------------------------------------------------------------------
static const CCoord kDefaultOnOffDashLength[] = {1, 1};
const CLineStyle kLineSolid {};
const CLineStyle kLineOnOffDash (CLineStyle::kLineCapButt, CLineStyle::kLineJoinMiter, 0, 2, kDefaultOnOffDashLength);
}
+69
View File
@@ -0,0 +1,69 @@
// 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
#pragma once
#include "vstguifwd.h"
#include <vector>
namespace VSTGUI {
//-----------
// @brief Line Style
//-----------
class CLineStyle
{
public:
using CoordVector = std::vector<CCoord>;
enum LineCap
{
kLineCapButt = 0,
kLineCapRound,
kLineCapSquare
};
enum LineJoin
{
kLineJoinMiter = 0,
kLineJoinRound,
kLineJoinBevel
};
CLineStyle () = default;
explicit CLineStyle (LineCap cap, LineJoin join = kLineJoinMiter, CCoord dashPhase = 0., uint32_t dashCount = 0, const CCoord* dashLengths = nullptr);
CLineStyle (LineCap cap, LineJoin join, CCoord dashPhase, const CoordVector& dashLengths);
CLineStyle (const CLineStyle& lineStyle);
~CLineStyle () noexcept = default;
CLineStyle (LineCap cap, LineJoin join, CCoord dashPhase, CoordVector&& dashLengths) noexcept;
CLineStyle (CLineStyle&& cls) noexcept;
CLineStyle& operator= (CLineStyle&& cls) noexcept;
LineCap getLineCap () const { return cap; }
LineJoin getLineJoin () const { return join; }
CCoord getDashPhase () const { return dashPhase; }
uint32_t getDashCount () const { return static_cast<uint32_t> (dashLengths.size ()); }
CoordVector& getDashLengths () { return dashLengths; }
const CoordVector& getDashLengths() const { return dashLengths; }
void setLineCap (LineCap newCap) { cap = newCap; }
void setLineJoin (LineJoin newJoin) { join = newJoin; }
void setDashPhase (CCoord phase) { dashPhase = phase; }
bool operator== (const CLineStyle& cls) const;
bool operator!= (const CLineStyle& cls) const { return !(*this == cls); }
CLineStyle& operator= (const CLineStyle& cls);
protected:
LineCap cap {kLineCapButt};
LineJoin join {kLineJoinMiter};
CCoord dashPhase {0.};
CoordVector dashLengths;
};
extern const CLineStyle kLineSolid;
extern const CLineStyle kLineOnOffDash;
} // VSTGUI
@@ -0,0 +1,99 @@
// 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 "coffscreencontext.h"
#include "cframe.h"
#include "cbitmap.h"
#include "platform/platformfactory.h"
#include "platform/iplatformgraphicsdevice.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
COffscreenContext::COffscreenContext (CBitmap* bitmap)
: CDrawContext (CRect (0, 0, bitmap->getWidth (), bitmap->getHeight ()))
, bitmap (bitmap)
{
}
//-----------------------------------------------------------------------------
COffscreenContext::COffscreenContext (const CRect& surfaceRect)
: CDrawContext (surfaceRect)
{
}
//-----------------------------------------------------------------------------
COffscreenContext::COffscreenContext (const PlatformGraphicsDeviceContextPtr device,
const CRect& surfaceRect,
const PlatformBitmapPtr& platformBitmap)
: CDrawContext (device, surfaceRect, platformBitmap->getScaleFactor ())
, bitmap (makeOwned<CBitmap> (platformBitmap))
{
}
//-----------------------------------------------------------------------------
void COffscreenContext::copyFrom (CDrawContext *pContext, CRect destRect, CPoint srcOffset)
{
if (bitmap)
bitmap->draw (pContext, destRect, srcOffset);
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//-----------------------------------------------------------------------------
SharedPointer<COffscreenContext> COffscreenContext::create (CFrame* frame, CCoord width, CCoord height, double scaleFactor)
{
return create ({width, height}, scaleFactor);
}
#endif
//-----------------------------------------------------------------------------
SharedPointer<COffscreenContext> COffscreenContext::create (const CPoint& size, double scaleFactor)
{
if (size.x >= 1. && size.y >= 1.)
{
if (auto graphicsDevice =
getPlatformFactory ().getGraphicsDeviceFactory ().getDeviceForScreen (
DefaultScreenIdentifier))
{
if (auto bitmap = getPlatformFactory ().createBitmap (size * scaleFactor))
{
bitmap->setScaleFactor (scaleFactor);
if (auto context = graphicsDevice->createBitmapContext (bitmap))
{
CRect surfaceRect (CPoint (), size * scaleFactor);
return makeOwned<COffscreenContext> (context, surfaceRect, bitmap);
}
}
}
}
return nullptr;
}
//-----------------------------------------------------------------------------
CCoord COffscreenContext::getWidth () const
{
return bitmap ? bitmap->getWidth () : 0.;
}
//-----------------------------------------------------------------------------
CCoord COffscreenContext::getHeight () const
{
return bitmap ? bitmap->getHeight () : 0.;
}
//-----------------------------------------------------------------------------
SharedPointer<CBitmap> renderBitmapOffscreen (
const CPoint& size, double scaleFactor,
const std::function<void (CDrawContext& drawContext)> drawCallback)
{
auto context = COffscreenContext::create (size, scaleFactor);
if (!context)
return nullptr;
context->beginDraw ();
drawCallback (*context);
context->endDraw ();
return shared (context->getBitmap ());
}
} // VSTGUI
@@ -0,0 +1,99 @@
// 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
#pragma once
#include "vstguifwd.h"
#include "cdrawcontext.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// COffscreenContext Declaration
//! @brief A draw context using a bitmap as it's back buffer
/*! @class COffscreenContext
There are two usage scenarios :
@section offscreen_usage1 Drawing into a bitmap and then push the contents into another draw context
@code
if (auto offscreen = COffscreenContext::create (frame, 100, 100))
{
offscreen->beginDraw ();
// ...
// draw into offscreen
// ...
offscreen->endDraw ();
offscreen->copyFrom (otherContext, destRect);
}
@endcode
@section offscreen_usage2 Drawing static content into a bitmap and reuse the bitmap for drawing
@code
if (cachedBitmap == 0)
{
if (auto offscreen = COffscreenContext::create (frame, 100, 100))
{
offscreen->beginDraw ();
// ...
// draw into offscreen
// ...
offscreen->endDraw ();
cachedBitmap = offscreen->getBitmap ();
if (cachedBitmap)
cachedBitmap->remember ();
}
}
if (cachedBitmap)
{
// ...
}
@endcode
*/
//-----------------------------------------------------------------------------
class COffscreenContext : public CDrawContext
{
public:
static SharedPointer<COffscreenContext> create (const CPoint& size, double scaleFactor = 1.);
VSTGUI_DEPRECATED (static SharedPointer<COffscreenContext> create (CFrame* frame, CCoord width,
CCoord height,
double scaleFactor = 1.);)
//-----------------------------------------------------------------------------
/// @name COffscreenContext Methods
//-----------------------------------------------------------------------------
//@{
/** copy from offscreen to pContext */
void copyFrom (CDrawContext *pContext, CRect destRect, CPoint srcOffset = CPoint (0, 0));
CCoord getWidth () const;
CCoord getHeight () const;
//@}
CBitmap* getBitmap () const { return bitmap; }
COffscreenContext (const PlatformGraphicsDeviceContextPtr device, const CRect& surfaceRect,
const PlatformBitmapPtr& platformBitmap);
protected:
explicit COffscreenContext (CBitmap* bitmap);
explicit COffscreenContext (const CRect& surfaceRect);
SharedPointer<CBitmap> bitmap;
};
//-----------------------------------------------------------------------------
/** Render a bitmap offscreen
* @param size size of the bitmap
* @param scaleFactor scale factor (bitmap size will be scaled by this)
* @param drawFunction user supplied draw function
* @return bitmap pointer on success and nullptr on failure
*/
SharedPointer<CBitmap> renderBitmapOffscreen (
const CPoint& size, double scaleFactor,
const std::function<void (CDrawContext& drawContext)> drawFunction);
} // VSTGUI
@@ -0,0 +1,294 @@
// 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 "cautoanimation.h"
#include "../algorithm.h"
#include "../cdrawcontext.h"
#include "../cbitmap.h"
namespace VSTGUI {
//------------------------------------------------------------------------
// CAutoAnimation
//------------------------------------------------------------------------
/*! @class CAutoAnimation
An auto-animation control contains a given number of subbitmaps which can be displayed in loop.
Two functions allows to get the previous or the next subbitmap (these functions increase or decrease
the current value of this control). Use a CMultiFrameBitmap for its background bitmap.
*/
// displays bitmaps within a (child-) window
//------------------------------------------------------------------------
/**
* CAutoAnimation constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background the bitmap
*/
//------------------------------------------------------------------------
CAutoAnimation::CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background)
: CControl (size, listener, tag, background)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
heightOfOneImage = size.getHeight ();
setNumSubPixmaps (background ? (int32_t)(background->getHeight () / heightOfOneImage) : 0);
totalHeightOfBitmap = heightOfOneImage * getNumSubPixmaps ();
#else
#endif
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
/**
* CAutoAnimation constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background the bitmap
* @param offset unused
*/
//------------------------------------------------------------------------
CAutoAnimation::CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background, const CPoint& offset)
: CControl (size, listener, tag, background), offset (offset)
{
heightOfOneImage = size.getHeight ();
setNumSubPixmaps (background ? (int32_t)(background->getHeight () / heightOfOneImage) : 0);
totalHeightOfBitmap = heightOfOneImage * getNumSubPixmaps ();
}
//------------------------------------------------------------------------
/**
* CAutoAnimation constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param subPixmaps number of sub bitmaps in background
* @param heightOfOneImage height of one sub bitmap
* @param background the bitmap
* @param offset unused
*/
//------------------------------------------------------------------------
CAutoAnimation::CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag,
int32_t subPixmaps, CCoord heightOfOneImage, CBitmap* background,
const CPoint& offset)
: CControl (size, listener, tag, background), offset (offset)
{
setNumSubPixmaps (subPixmaps);
setHeightOfOneImage (heightOfOneImage);
totalHeightOfBitmap = heightOfOneImage * getNumSubPixmaps ();
setMin (0.f);
setMax ((float)(totalHeightOfBitmap - (heightOfOneImage + 1.)));
}
//------------------------------------------------------------------------
void CAutoAnimation::setBitmapOffset (const CPoint& off)
{
offset = off;
invalid ();
}
//------------------------------------------------------------------------
CPoint CAutoAnimation::getBitmapOffset () const { return offset; }
#endif // VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
CAutoAnimation::CAutoAnimation (const CAutoAnimation& v)
: CControl (v)
#if VSTGUI_ENABLE_DEPRECATED_METHODS
, offset (v.offset)
, totalHeightOfBitmap (v.totalHeightOfBitmap)
#endif
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
setNumSubPixmaps (v.subPixmaps);
setHeightOfOneImage (v.heightOfOneImage);
#endif
}
//------------------------------------------------------------------------
bool CAutoAnimation::isWindowOpened () const { return bWindowOpened; }
//------------------------------------------------------------------------
void CAutoAnimation::draw (CDrawContext *pContext)
{
if (isWindowOpened ())
{
if (auto bitmap = getDrawBackground ())
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
auto frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
}
else
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint where;
where.y = (int32_t)value + offset.y;
where.x = offset.x;
bitmap->draw (pContext, getViewSize (), where);
#else
CView::draw (pContext);
#endif
}
}
}
setDirty (false);
}
//------------------------------------------------------------------------
CMouseEventResult CAutoAnimation::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (buttons & kLButton)
{
if (!isWindowOpened ())
{
value = 0;
openWindow ();
invalid ();
valueChanged ();
}
else
{
// stop info animation
value = 0; // draw first pic of bitmap
invalid ();
closeWindow ();
valueChanged ();
}
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
bool CAutoAnimation::attached (CView* parent)
{
if (CControl::attached (parent))
{
if (animationFrameTime > 0 && isWindowOpened ())
startTimer ();
return true;
}
return false;
}
//------------------------------------------------------------------------
bool CAutoAnimation::removed (CView* parent)
{
timer = nullptr;
return CControl::removed (parent);
}
//------------------------------------------------------------------------
void CAutoAnimation::startTimer ()
{
if (animationFrameTime > 0)
{
timer = makeOwned<CVSTGUITimer> (
[this] (auto*) {
nextPixmap ();
invalid ();
},
animationFrameTime, true);
}
}
//------------------------------------------------------------------------
void CAutoAnimation::openWindow ()
{
bWindowOpened = true;
if (isAttached ())
startTimer ();
}
//------------------------------------------------------------------------
void CAutoAnimation::closeWindow ()
{
bWindowOpened = false;
timer = nullptr;
}
//------------------------------------------------------------------------
void CAutoAnimation::updateMinMaxFromBackground ()
{
if (auto bitmap = getDrawBackground ())
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
auto numFrames = getMultiFrameBitmapRangeLength (*mfb);
setMin (0.f);
setMax (numFrames);
#if VSTGUI_ENABLE_DEPRECATED_METHODS
heightOfOneImage = mfb->getFrameSize ().y;
totalHeightOfBitmap = heightOfOneImage * numFrames;
#endif
}
}
}
//------------------------------------------------------------------------
void CAutoAnimation::setBackground (CBitmap* background)
{
CControl::setBackground (background);
updateMinMaxFromBackground ();
}
//------------------------------------------------------------------------
void CAutoAnimation::nextPixmap ()
{
if (auto bitmap = getDrawBackground ())
{
if (dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
if (getValue () == getMax ())
setValue (getMin ());
else
setValue (getValue () + 1.f);
return;
}
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
value += (float)heightOfOneImage;
if (value >= (totalHeightOfBitmap - heightOfOneImage))
value = 0;
#endif
}
//------------------------------------------------------------------------
void CAutoAnimation::previousPixmap ()
{
if (auto bitmap = getDrawBackground ())
{
if (dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
if (getValue () == getMin ())
setValue (getMax ());
else
setValue (getValue () - 1.f);
return;
}
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
value -= (float)heightOfOneImage;
if (value < 0.f)
value = (float)(totalHeightOfBitmap - heightOfOneImage - 1);
#endif
}
//------------------------------------------------------------------------
void CAutoAnimation::setAnimationTime (uint32_t animationTime)
{
animationFrameTime = animationTime;
if (timer)
startTimer ();
}
//------------------------------------------------------------------------
uint32_t CAutoAnimation::getAnimationTime () const { return animationFrameTime; }
} // VSTGUI
@@ -0,0 +1,90 @@
// 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
#pragma once
#include "ccontrol.h"
#include "../cbitmap.h"
#include "../cvstguitimer.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CAutoAnimation Declaration
//!
/// @ingroup controls uses_multi_frame_bitmaps
//-----------------------------------------------------------------------------
class CAutoAnimation : public CControl,
public MultiFrameBitmapView<CAutoAnimation>
#if VSTGUI_ENABLE_DEPRECATED_METHODS
,
public IMultiBitmapControl
#endif
{
public:
CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background);
CAutoAnimation (const CAutoAnimation& autoAnimation);
void draw (CDrawContext*) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
bool attached (CView* parent) override;
bool removed (CView* parent) override;
//-----------------------------------------------------------------------------
/// @name CAutoAnimation Methods
//-----------------------------------------------------------------------------
//@{
/** enabled drawing */
virtual void openWindow ();
/** disable drawing */
virtual void closeWindow ();
/** the next sub bitmap should be displayed */
virtual void nextPixmap ();
/** the previous sub bitmap should be displayed */
virtual void previousPixmap ();
bool isWindowOpened () const;
void setAnimationTime (uint32_t animationTime);
uint32_t getAnimationTime () const;
//@}
void setBackground (CBitmap* background) override;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background,
const CPoint& offset);
CAutoAnimation (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
CCoord heightOfOneImage, CBitmap* background,
const CPoint& offset = CPoint (0, 0));
void setNumSubPixmaps (int32_t numSubPixmaps) override
{
IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps);
invalid ();
}
void setBitmapOffset (const CPoint& off);
CPoint getBitmapOffset () const;
#endif
CLASS_METHODS(CAutoAnimation, CControl)
protected:
~CAutoAnimation () noexcept override = default;
void updateMinMaxFromBackground ();
void startTimer ();
uint32_t animationFrameTime {0u};
SharedPointer<CVSTGUITimer> timer;
bool bWindowOpened {false};
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint offset {};
CCoord totalHeightOfBitmap {0};
#endif
};
} // VSTGUI
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,288 @@
// 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
#pragma once
#include "ccontrol.h"
#include "../cfont.h"
#include "../ccolor.h"
#include "../cbitmap.h"
#include "../cgradient.h"
#include "../cgraphicspath.h"
#include "../cstring.h"
#include "../cdrawmethods.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// COnOffButton Declaration
//! @brief a button control with 2 states
/// @ingroup controls
//-----------------------------------------------------------------------------
class COnOffButton : public CControl
{
public:
COnOffButton (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1, CBitmap* background = nullptr, int32_t style = 0);
COnOffButton (const COnOffButton& onOffButton);
//-----------------------------------------------------------------------------
/// @name COnOffButton Methods
//-----------------------------------------------------------------------------
//@{
virtual int32_t getStyle () const { return style; }
virtual void setStyle (int32_t newStyle) { style = newStyle; }
//@}
// overrides
void draw (CDrawContext*) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
void onKeyboardEvent (KeyboardEvent& event) override;
bool sizeToFit () override;
CLASS_METHODS(COnOffButton, CControl)
protected:
~COnOffButton () noexcept override = default;
int32_t style;
};
//-----------------------------------------------------------------------------
// CCheckBox Declaration
/// @brief a check box control with a title and 3 states
/// @ingroup controls
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class CCheckBox : public CControl
{
public:
CCheckBox (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1, UTF8StringPtr title = nullptr, CBitmap* bitmap = nullptr, int32_t style = 0);
CCheckBox (const CCheckBox& checkbox);
enum Styles
{
/** automatically adjusts the width so that the label is completely visible */
kAutoSizeToFit = 1 << 0,
/** draws a crossbox instead of a checkmark if no bitmap is provided */
kDrawCrossBox = 1 << 1,
/** do not limit the box drawing to the cap height */
kIgnoreCapHeightOnDraw = 1 << 2,
};
//-----------------------------------------------------------------------------
/// @name CCheckBox Methods
//-----------------------------------------------------------------------------
//@{
virtual void setTitle (const UTF8String& newTitle);
const UTF8String& getTitle () const { return title; }
virtual void setFont (CFontRef newFont);
const CFontRef getFont () const { return font; }
virtual void setFontColor (const CColor& newColor) { fontColor = newColor; invalid (); }
const CColor& getFontColor () const { return fontColor; }
virtual void setBoxFrameColor (const CColor& newColor) { boxFrameColor = newColor; invalid (); }
const CColor& getBoxFrameColor () const { return boxFrameColor; }
virtual void setBoxFillColor (const CColor& newColor) { boxFillColor = newColor; invalid (); }
const CColor& getBoxFillColor () const { return boxFillColor; }
virtual void setCheckMarkColor (const CColor& newColor) { checkMarkColor = newColor; invalid (); }
const CColor& getCheckMarkColor () const { return checkMarkColor; }
virtual int32_t getStyle () const { return style; }
virtual void setStyle (int32_t newStyle);
CCoord getFrameWidth () const { return frameWidth; }
virtual void setFrameWidth (CCoord width);
CCoord getRoundRectRadius () const { return roundRectRadius; }
virtual void setRoundRectRadius (CCoord radius);
//@}
// overrides
void draw (CDrawContext* context) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
void onKeyboardEvent (KeyboardEvent& event) override;
bool sizeToFit () override;
void setBackground (CBitmap *background) override;
bool getFocusPath (CGraphicsPath& outPath) override;
CLASS_METHODS(CCheckBox, CControl)
protected:
~CCheckBox () noexcept override = default;
UTF8String title;
int32_t style;
CColor fontColor;
CColor boxFrameColor;
CColor boxFillColor;
CColor checkMarkColor;
CCoord frameWidth {1};
CCoord roundRectRadius {0};
SharedPointer<CFontDesc> font;
private:
float previousValue {0.f};
bool hilight {false};
};
//-----------------------------------------------------------------------------
// CKickButton Declaration
//!
/// @ingroup controls uses_multi_frame_bitmaps
//-----------------------------------------------------------------------------
class CKickButton : public CControl,
public MultiFrameBitmapView<CKickButton>
#if VSTGUI_ENABLE_DEPRECATED_METHODS
,
public IMultiBitmapControl
#endif
{
public:
CKickButton (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
CKickButton (const CKickButton& kickButton);
void draw (CDrawContext*) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
void onKeyboardEvent (KeyboardEvent& event) override;
bool sizeToFit () override;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
void setNumSubPixmaps (int32_t numSubPixmaps) override { IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps); invalid (); }
CKickButton (const CRect& size, IControlListener* listener, int32_t tag,
CCoord heightOfOneImage, CBitmap* background,
const CPoint& offset = CPoint (0, 0));
#endif
CLASS_METHODS(CKickButton, CControl)
protected:
~CKickButton () noexcept override = default;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint offset {};
#endif
};
//-----------------------------------------------------------------------------
// CTextButton Declaration
/// @brief a button which renders without bitmaps
/// @ingroup controls
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class CTextButton : public CControl
{
public:
/** CTextButton style */
enum Style
{
kKickStyle = 0,
kOnOffStyle
};
CTextButton (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1, UTF8StringPtr title = nullptr, Style = kKickStyle);
//-----------------------------------------------------------------------------
/// @name CTextButton Methods
//-----------------------------------------------------------------------------
//@{
virtual void setTitle (const UTF8String& newTitle);
const UTF8String& getTitle () const { return title; }
virtual void setFont (CFontRef newFont);
CFontRef getFont () const { return font; }
virtual void setTextColor (const CColor& color);
const CColor& getTextColor () const { return textColor; }
virtual void setTextColorHighlighted (const CColor& color);
const CColor& getTextColorHighlighted () const { return textColorHighlighted; }
virtual void setGradient (CGradient* gradient);
CGradient* getGradient () const;
virtual void setGradientHighlighted (CGradient* gradient);
CGradient* getGradientHighlighted () const;
virtual void setFrameColor (const CColor& color);
const CColor& getFrameColor () const { return frameColor; }
virtual void setFrameColorHighlighted (const CColor& color);
const CColor& getFrameColorHighlighted () const { return frameColorHighlighted; }
virtual void setFrameWidth (CCoord width);
CCoord getFrameWidth () const { return frameWidth; }
virtual void setRoundRadius (CCoord radius);
CCoord getRoundRadius () const { return roundRadius; }
virtual void setStyle (Style style);
Style getStyle () const { return style; }
virtual void setIcon (CBitmap* bitmap);
CBitmap* getIcon () const;
virtual void setIconHighlighted (CBitmap* bitmap);
CBitmap* getIconHighlighted () const;
virtual void setIconPosition (CDrawMethods::IconPosition pos);
CDrawMethods::IconPosition getIconPosition () const { return iconPosition; }
virtual void setTextMargin (CCoord margin);
CCoord getTextMargin () const { return textMargin; }
virtual void setTextAlignment (CHoriTxtAlign hAlign);
CHoriTxtAlign getTextAlignment () const { return horiTxtAlign; }
//@}
// overrides
void draw (CDrawContext* context) override;
bool getFocusPath (CGraphicsPath& outPath) override;
bool drawFocusOnTop () override;
void setViewSize (const CRect& rect, bool invalid = true) override;
bool removed (CView* parent) override;
bool sizeToFit () override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
void onKeyboardEvent (KeyboardEvent& event) override;
CLASS_METHODS_NOCOPY (CTextButton, CControl)
protected:
~CTextButton () noexcept override = default;
void invalidPath ();
CGraphicsPath* getPath (CDrawContext* context, CCoord lineWidth);
SharedPointer<CFontDesc> font;
SharedPointer<CGraphicsPath> _path;
SharedPointer<CBitmap> icon;
SharedPointer<CBitmap> iconHighlighted;
SharedPointer<CGradient> gradient;
SharedPointer<CGradient> gradientHighlighted;
CColor textColor;
CColor frameColor;
CColor textColorHighlighted;
CColor frameColorHighlighted;
CCoord frameWidth;
CCoord roundRadius;
CCoord textMargin;
CHoriTxtAlign horiTxtAlign;
CDrawMethods::IconPosition iconPosition;
Style style;
UTF8String title;
private:
float fEntryState;
};
} // VSTGUI
@@ -0,0 +1,593 @@
// 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 "ccolorchooser.h"
#include "cslider.h"
#include "ctextlabel.h"
#include "ccontrol.h"
#include "../cdrawcontext.h"
#include "../cframe.h"
#include "../idatapackage.h"
#include "../dragging.h"
#include <string>
namespace VSTGUI {
/// @cond ignore
namespace CColorChooserInternal {
//-----------------------------------------------------------------------------
class Slider : public CSlider
{
public:
Slider (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1)
: CSlider (size, listener, tag, 0, 0, nullptr, nullptr)
{
if (size.getWidth () > size.getHeight ())
setHandleSizePrivate (size.getHeight (), size.getHeight ());
else
setHandleSizePrivate (size.getWidth (), size.getWidth ());
const CRect& r (size);
setViewSize (r, false);
setWheelInc (10.f/255.f);
}
void draw (CDrawContext* context) override
{
CColor handleFillColor (kWhiteCColor);
CColor handleFrameColor (kBlackCColor);
CColor backgroundFillColor (kGreyCColor);
CColor backgroundFrameColor (kBlackCColor);
CColor bandColor (kTransparentCColor);
CCoord backgroundFrameWidth = 1;
CCoord handleFrameWidth = 1;
auto controlSize = getControlSizePrivate ();
auto sliderSize = getHandleSizePrivate ();
CRect backgroundRect;
backgroundRect.setSize (controlSize);
backgroundRect.offset (getViewSize ().left, getViewSize ().top);
context->setDrawMode (kAntiAliasing);
context->setFillColor (backgroundFillColor);
context->setFrameColor (backgroundFrameColor);
context->setLineWidth (backgroundFrameWidth);
context->setLineStyle (kLineSolid);
context->drawRect (backgroundRect, kDrawFilledAndStroked);
if (getStyle () & kHorizontal)
{
backgroundRect.left += getOffsetHandle ().x + sliderSize.x / 2;
backgroundRect.right -= getOffsetHandle ().x + sliderSize.x / 2;
backgroundRect.top += controlSize.y / 2 - 2;
backgroundRect.bottom -= controlSize.y / 2 - 2;
}
else
{
backgroundRect.left += controlSize.x / 2 - 2;
backgroundRect.right -= controlSize.x / 2 - 2;
backgroundRect.top += getOffsetHandle ().y + sliderSize.y / 2;
backgroundRect.bottom -= getOffsetHandle ().y + sliderSize.y / 2;
}
context->setFillColor (bandColor);
context->drawRect (backgroundRect, kDrawFilled);
// calc new coords of slider
CRect rectNew = calculateHandleRect (getValueNormalized ());
context->setFillColor (handleFillColor);
context->setFrameColor (handleFrameColor);
context->setLineWidth (handleFrameWidth);
context->drawRect (rectNew, kDrawFilledAndStroked);
setDirty (false);
}
};
//-----------------------------------------------------------------------------
class ColorView : public CControl, public IDropTarget
{
public:
ColorView (const CRect& r, const CColor& initialColor, IControlListener* listener = nullptr, int32_t tag = -1, bool checkerBoardBack = true, const CColor& checkerBoardColor1 = kWhiteCColor, const CColor& checkerBoardColor2 = kBlackCColor)
: CControl (r, listener, tag)
, color (initialColor)
, checkerBoardColor1 (checkerBoardColor1)
, checkerBoardColor2 (checkerBoardColor2)
, checkerBoardBack (checkerBoardBack)
{
}
void draw (CDrawContext* context) override
{
context->setDrawMode (kAliasing);
if (checkerBoardBack && color.alpha != 255)
{
context->setFillColor (checkerBoardColor1);
context->drawRect (getViewSize (), kDrawFilled);
context->setFillColor (checkerBoardColor2);
CRect r (getViewSize ().left, getViewSize ().top, getViewSize ().left + 5, getViewSize ().top + 5);
for (int32_t x = 0; x < getViewSize ().getWidth (); x+=5)
{
r.left = getViewSize ().left + x;
r.top = (x % 2) ? getViewSize ().top : getViewSize ().top + 5;
r.right = r.left + 5;
r.bottom = r.top + 5;
for (int32_t y = 0; y < getViewSize ().getHeight (); y+=10)
{
context->drawRect (r, kDrawFilled);
r.offset (0, 10);
}
}
}
context->setLineWidth (1);
context->setFillColor (color);
context->setFrameColor (kBlackCColor);
context->drawRect (getViewSize (), kDrawFilledAndStroked);
setDirty (false);
}
const CColor& getColor () const { return color; }
void setColor (const CColor& newColor)
{
color = newColor;
}
// we accept strings which look like : '#ff3355' (rgb) and '#ff3355bb' (rgba)
static bool dragContainerHasColor (IDataPackage* drag, CColor* color)
{
for (auto item : drag)
{
if (item.type != IDataPackage::kText)
continue;
std::string colorString (static_cast<const char*> (item.data), item.dataSize);
if (colorString.length () == 7)
{
if (colorString[0] == '#')
{
if (color)
{
std::string rv (colorString.substr (1, 2));
std::string gv (colorString.substr (3, 2));
std::string bv (colorString.substr (5, 2));
color->red = (uint8_t)strtol (rv.c_str (), nullptr, 16);
color->green = (uint8_t)strtol (gv.c_str (), nullptr, 16);
color->blue = (uint8_t)strtol (bv.c_str (), nullptr, 16);
color->alpha = 255;
}
return true;
}
}
if (colorString.length () == 9)
{
if (colorString[0] == '#')
{
if (color)
{
std::string rv (colorString.substr (1, 2));
std::string gv (colorString.substr (3, 2));
std::string bv (colorString.substr (5, 2));
std::string av (colorString.substr (7, 2));
color->red = (uint8_t)strtol (rv.c_str (), nullptr, 16);
color->green = (uint8_t)strtol (gv.c_str (), nullptr, 16);
color->blue = (uint8_t)strtol (bv.c_str (), nullptr, 16);
color->alpha = (uint8_t)strtol (av.c_str (), nullptr, 16);
}
return true;
}
}
}
return false;
}
SharedPointer<IDropTarget> getDropTarget () override { return this; }
bool onDrop (DragEventData data) override
{
CColor dragColor;
if (dragContainerHasColor (data.drag, &dragColor))
{
setColor (dragColor);
valueChanged ();
return true;
}
return false;
}
DragOperation onDragEnter (DragEventData data) override
{
dragOperation =
dragContainerHasColor (data.drag, nullptr) ? DragOperation::Copy : DragOperation::None;
return dragOperation;
}
DragOperation onDragMove (DragEventData data) override
{
return dragOperation;
}
void onDragLeave (DragEventData data) override
{
dragOperation = DragOperation::None;
}
CLASS_METHODS(ColorView, CControl)
protected:
DragOperation dragOperation {DragOperation::None};
CColor color;
CColor checkerBoardColor1;
CColor checkerBoardColor2;
bool checkerBoardBack;
};
//-----------------------------------------------------------------------------
static void setupParamDisplay (CParamDisplay* display, const CColorChooserUISettings& settings)
{
display->setFont (settings.font);
display->setFontColor (settings.fontColor);
display->setTransparency (true);
}
} // CColorChooserInternal
/// @endcond
//-----------------------------------------------------------------------------
bool CColorChooser::convertNormalizedToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData)
{
snprintf (string, 255, "%.3f", value);
return true;
}
//-----------------------------------------------------------------------------
bool CColorChooser::convertColorValueToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData)
{
snprintf (string, 255, "%d", (int32_t)(value * 255.f));
return true;
}
//-----------------------------------------------------------------------------
bool CColorChooser::convertAngleToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData)
{
snprintf (string, 255, "%d%s", (int32_t)(value * 359.f), kDegreeSymbol);
return true;
}
//-----------------------------------------------------------------------------
bool CColorChooser::convertNormalized (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData)
{
output = UTF8StringView (string).toFloat ();
if (output < 0.f)
output = 0.f;
else if (output > 1.f)
output = 1.f;
return true;
}
//-----------------------------------------------------------------------------
bool CColorChooser::convertColorValue (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData)
{
output = UTF8StringView (string).toFloat ();
if (output < 0.f)
output = 0.f;
else if (output > 255.f)
output = 255.f;
output /= 255.f;
return true;
}
//-----------------------------------------------------------------------------
bool CColorChooser::convertAngle (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData)
{
output = UTF8StringView (string).toFloat ();
if (output < 0.f)
output = 0.f;
else if (output > 359.f)
output = 359.f;
output /= 359.f;
return true;
}
//-----------------------------------------------------------------------------
CColorChooser::CColorChooser (IColorChooserDelegate* delegate, const CColor& initialColor, const CColorChooserUISettings& settings)
: CViewContainer (CRect (0, 0, 0, 0))
, delegate (delegate)
, color (initialColor)
, redSlider (nullptr)
, greenSlider (nullptr)
, blueSlider (nullptr)
, hueSlider (nullptr)
, saturationSlider (nullptr)
, brightnessSlider (nullptr)
, alphaSlider (nullptr)
, colorView (nullptr)
{
setTransparency (true);
setAutosizeFlags (kAutosizeAll);
const CCoord controlHeight = settings.font->getSize () + 2;
const CCoord controlWidth = 150;
const CCoord editWidth = 40;
const CCoord labelWidth = 40;
const CCoord xMargin = settings.margin.x;
const CCoord yMargin = settings.margin.y;
colorView = new CColorChooserInternal::ColorView (CRect (1, 1, labelWidth + xMargin + controlWidth + xMargin + editWidth, 100), initialColor, this, kColorTag, settings.checkerBoardBack, settings.checkerBoardColor1, settings.checkerBoardColor2);
colorView->setAutosizeFlags (kAutosizeAll);
addView (colorView);
CRect r (colorView->getViewSize ());
r.offset (labelWidth + xMargin, r.bottom + yMargin);
r.setWidth (controlWidth);
r.setHeight (controlHeight);
redSlider = new CColorChooserInternal::Slider (r, this, kRedTag);
redSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
addView (redSlider);
r.offset (0, yMargin + controlHeight);
greenSlider = new CColorChooserInternal::Slider (r, this, kGreenTag);
greenSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
addView (greenSlider);
r.offset (0, yMargin + controlHeight);
blueSlider = new CColorChooserInternal::Slider (r, this, kBlueTag);
blueSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
addView (blueSlider);
r.offset (0, yMargin + yMargin + controlHeight);
hueSlider = new CColorChooserInternal::Slider (r, this, kHueTag);
hueSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
addView (hueSlider);
r.offset (0, yMargin + controlHeight);
saturationSlider = new CColorChooserInternal::Slider (r, this, kSaturationTag);
saturationSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
addView (saturationSlider);
r.offset (0, yMargin + controlHeight);
brightnessSlider = new CColorChooserInternal::Slider (r, this, kBrightnessTag);
brightnessSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
addView (brightnessSlider);
r.offset (0, yMargin + yMargin + controlHeight);
alphaSlider = new CColorChooserInternal::Slider (r, this, kAlphaTag);
alphaSlider->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom);
addView (alphaSlider);
CRect newSize (getViewSize ());
newSize.bottom = r.bottom+1;
newSize.right = colorView->getViewSize ().right+2;
setAutosizingEnabled (false);
setViewSize (newSize);
setMouseableArea (newSize);
setAutosizingEnabled (true);
r = colorView->getViewSize ();
r.offset (0, r.bottom + yMargin);
r.setWidth (labelWidth);
r.setHeight (controlHeight);
auto* label = new CTextLabel (r, "Red");
CColorChooserInternal::setupParamDisplay (label, settings);
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
addView (label);
r.offset (0, yMargin + controlHeight);
label = new CTextLabel (r, "Green");
CColorChooserInternal::setupParamDisplay (label, settings);
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
addView (label);
r.offset (0, yMargin + controlHeight);
label = new CTextLabel (r, "Blue");
CColorChooserInternal::setupParamDisplay (label, settings);
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
addView (label);
r.offset (0, yMargin + yMargin + controlHeight);
label = new CTextLabel (r, "Hue");
CColorChooserInternal::setupParamDisplay (label, settings);
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
addView (label);
r.offset (0, yMargin + controlHeight);
label = new CTextLabel (r, "Sat");
CColorChooserInternal::setupParamDisplay (label, settings);
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
addView (label);
r.offset (0, yMargin + controlHeight);
label = new CTextLabel (r, "Value");
CColorChooserInternal::setupParamDisplay (label, settings);
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
addView (label);
r.offset (0, yMargin + yMargin + controlHeight);
label = new CTextLabel (r, "Alpha");
CColorChooserInternal::setupParamDisplay (label, settings);
label->setAutosizeFlags (kAutosizeLeft|kAutosizeBottom);
addView (label);
r = colorView->getViewSize ();
r.offset (labelWidth + xMargin + controlWidth + xMargin, r.bottom + yMargin);
r.setWidth (editWidth);
r.setHeight (controlHeight);
editFields[0] = new CTextEdit (r, this, kRedTag, nullptr);
CColorChooserInternal::setupParamDisplay (editFields[0], settings);
editFields[0]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
editFields[0]->setStringToValueFunction (convertColorValue);
editFields[0]->setValueToStringFunction (convertColorValueToString);
addView (editFields[0]);
r.offset (0, yMargin + controlHeight);
editFields[1] = new CTextEdit (r, this, kGreenTag, nullptr);
CColorChooserInternal::setupParamDisplay (editFields[1], settings);
editFields[1]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
editFields[1]->setStringToValueFunction (convertColorValue);
editFields[1]->setValueToStringFunction (convertColorValueToString);
addView (editFields[1]);
r.offset (0, yMargin + controlHeight);
editFields[2] = new CTextEdit (r, this, kBlueTag, nullptr);
CColorChooserInternal::setupParamDisplay (editFields[2], settings);
editFields[2]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
editFields[2]->setStringToValueFunction (convertColorValue);
editFields[2]->setValueToStringFunction (convertColorValueToString);
addView (editFields[2]);
r.offset (0, yMargin + yMargin + controlHeight);
editFields[3] = new CTextEdit (r, this, kHueTag, nullptr);
CColorChooserInternal::setupParamDisplay (editFields[3], settings);
editFields[3]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
editFields[3]->setStringToValueFunction (convertColorValue);
editFields[3]->setValueToStringFunction (convertColorValueToString);
addView (editFields[3]);
r.offset (0, yMargin + controlHeight);
editFields[4] = new CTextEdit (r, this, kSaturationTag, nullptr);
CColorChooserInternal::setupParamDisplay (editFields[4], settings);
editFields[4]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
editFields[4]->setStringToValueFunction (convertColorValue);
editFields[4]->setValueToStringFunction (convertColorValueToString);
addView (editFields[4]);
r.offset (0, yMargin + controlHeight);
editFields[5] = new CTextEdit (r, this, kBrightnessTag, nullptr);
CColorChooserInternal::setupParamDisplay (editFields[5], settings);
editFields[5]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
editFields[5]->setStringToValueFunction (convertColorValue);
editFields[5]->setValueToStringFunction (convertColorValueToString);
addView (editFields[5]);
r.offset (0, yMargin + yMargin + controlHeight);
editFields[6] = new CTextEdit (r, this, kAlphaTag, nullptr);
CColorChooserInternal::setupParamDisplay (editFields[6], settings);
editFields[6]->setAutosizeFlags (kAutosizeRight|kAutosizeBottom);
editFields[6]->setStringToValueFunction (convertColorValue);
editFields[6]->setValueToStringFunction (convertColorValueToString);
addView (editFields[6]);
updateState ();
}
//-----------------------------------------------------------------------------
void CColorChooser::valueChanged (CControl* control)
{
switch (control->getTag ())
{
case kRedTag:
{
color.setNormRed (control->getValue ());
break;
}
case kGreenTag:
{
color.setNormGreen (control->getValue ());
break;
}
case kBlueTag:
{
color.setNormBlue (control->getValue ());
break;
}
case kAlphaTag:
{
color.setNormAlpha (control->getValue ());
break;
}
case kHueTag:
{
double hue, saturation, value;
color.toHSV (hue, saturation, value);
hue = control->getValue () * 359.;
color.fromHSV (hue, saturation, value);
break;
}
case kSaturationTag:
{
double hue, saturation, value;
color.toHSV (hue, saturation, value);
saturation = control->getValue ();
color.fromHSV (hue, saturation, value);
break;
}
case kBrightnessTag:
{
double hue, saturation, value;
color.toHSV (hue, saturation, value);
value = control->getValue ();
color.fromHSV (hue, saturation, value);
break;
}
case kColorTag:
{
color = colorView->getColor ();
}
}
updateState ();
if (delegate)
delegate->colorChanged (this, color);
}
//-----------------------------------------------------------------------------
void CColorChooser::controlBeginEdit (CControl* pControl)
{
if (delegate)
delegate->onBeginColorChange (this);
}
//-----------------------------------------------------------------------------
void CColorChooser::controlEndEdit (CControl* pControl)
{
if (delegate)
delegate->onEndColorChange (this);
}
//-----------------------------------------------------------------------------
void CColorChooser::setColor (const CColor& newColor)
{
color = newColor;
updateState ();
}
//-----------------------------------------------------------------------------
void CColorChooser::updateState ()
{
double hue, saturation, value;
color.toHSV (hue, saturation, value);
redSlider->setValue (color.normRed<float> ());
greenSlider->setValue (color.normGreen<float> ());
blueSlider->setValue (color.normBlue<float> ());
alphaSlider->setValue (color.normAlpha<float> ());
hueSlider->setValue ((float)(hue / 359.));
saturationSlider->setValue ((float)saturation);
brightnessSlider->setValue ((float)value);
colorView->setColor (color);
editFields[0]->setValue (redSlider->getValue ());
editFields[1]->setValue (greenSlider->getValue ());
editFields[2]->setValue (blueSlider->getValue ());
editFields[3]->setValue (hueSlider->getValue ());
editFields[4]->setValue (saturationSlider->getValue ());
editFields[5]->setValue (brightnessSlider->getValue ());
editFields[6]->setValue (alphaSlider->getValue ());
for (int32_t i = 0; i < 7; i++)
editFields[i]->invalid ();
redSlider->invalid ();
greenSlider->invalid ();
blueSlider->invalid ();
alphaSlider->invalid ();
hueSlider->invalid ();
saturationSlider->invalid ();
brightnessSlider->invalid ();
colorView->invalid ();
}
} // VSTGUI
@@ -0,0 +1,95 @@
// 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
#pragma once
#include "../vstguifwd.h"
#include "../cviewcontainer.h"
#include "icontrollistener.h"
#include "ctextedit.h"
namespace VSTGUI {
/// @cond ignore
namespace CColorChooserInternal {
class ColorView;
}
/// @endcond
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class IColorChooserDelegate
{
public:
virtual void colorChanged (CColorChooser* chooser, const CColor& color) = 0;
virtual void onBeginColorChange (CColorChooser* chooser) = 0;
virtual void onEndColorChange (CColorChooser* chooser) = 0;
};
//-----------------------------------------------------------------------------
struct CColorChooserUISettings
{
CFontRef font {kNormalFont};
CColor fontColor {kWhiteCColor};
CColor checkerBoardColor1 {kWhiteCColor};
CColor checkerBoardColor2 {kBlackCColor};
CPoint margin {5, 5};
bool checkerBoardBack {true};
};
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class CColorChooser : public CViewContainer, public IControlListener
{
public:
CColorChooser (IColorChooserDelegate* delegate = nullptr, const CColor& initialColor = kTransparentCColor, const CColorChooserUISettings& settings = CColorChooserUISettings ());
~CColorChooser () noexcept override = default;
void setColor (const CColor& newColor);
//-----------------------------------------------------------------------------
protected:
void valueChanged (CControl* pControl) override;
void controlBeginEdit (CControl* pControl) override;
void controlEndEdit (CControl* pControl) override;
void updateState ();
/// @cond ignore
IColorChooserDelegate* delegate;
CColor color;
CSlider* redSlider;
CSlider* greenSlider;
CSlider* blueSlider;
CSlider* hueSlider;
CSlider* saturationSlider;
CSlider* brightnessSlider;
CSlider* alphaSlider;
CTextEdit* editFields[8];
CColorChooserInternal::ColorView* colorView;
//-----------------------------------------------------------------------------
enum {
kRedTag = 10000,
kGreenTag,
kBlueTag,
kHueTag,
kSaturationTag,
kBrightnessTag,
kAlphaTag,
kColorTag
};
//-----------------------------------------------------------------------------
static bool convertNormalized (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData);
static bool convertColorValue (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData);
static bool convertAngle (UTF8StringPtr string, float& output, CTextEdit::StringToValueUserData* userData);
static bool convertNormalizedToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData);
static bool convertColorValueToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData);
static bool convertAngleToString (float value, char string[256], CParamDisplay::ValueToStringUserData* userData);
/// @endcond
};
} // VSTGUI
@@ -0,0 +1,390 @@
// 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 "ccontrol.h"
#include "icontrollistener.h"
#include "../algorithm.h"
#include "../events.h"
#include "../cframe.h"
#include "../cgraphicspath.h"
#include "../cvstguitimer.h"
#include "../dispatchlist.h"
#include "../iviewlistener.h"
#include <cassert>
#define VSTGUI_CCONTROL_LOG_EDITING 0 //DEBUG
namespace VSTGUI {
//------------------------------------------------------------------------
struct CControl::Impl : ViewEventListenerAdapter
{
using SubListenerDispatcher = DispatchList<IControlListener*>;
SubListenerDispatcher subListeners;
float oldValue {1};
float defaultValue {0.5};
float vmin {0};
float vmax {1.f};
float wheelInc {0.1f};
int32_t editing {0};
void viewOnEvent (CView* view, Event& event) override
{
if (event.type != EventType::MouseDown)
return;
auto control = static_cast<CControl*> (view);
auto& mouseDownEvent = castMouseDownEvent (event);
if (CControl::CheckDefaultValueEventFunc (control, mouseDownEvent))
{
auto defValue = control->getDefaultValue ();
if (defValue != control->getValue ())
{
control->beginEdit ();
control->setValue (defValue);
control->valueChanged ();
control->endEdit ();
control->setDirty ();
}
mouseDownEvent.consumed = true;
mouseDownEvent.ignoreFollowUpMoveAndUpEvents (true);
}
}
};
//------------------------------------------------------------------------
// CControl
//------------------------------------------------------------------------
/*! @class CControl
This object manages the tag identification and the value of a control object.
*/
CControl::CControl (const CRect& size, IControlListener* listener, int32_t tag, CBitmap *pBackground)
: CView (size)
, listener (listener)
, tag (tag)
, value (0)
{
impl = std::unique_ptr<Impl> (new Impl);
setTransparency (false);
setMouseEnabled (true);
setBackground (pBackground);
registerViewEventListener (impl.get ());
}
//------------------------------------------------------------------------
CControl::CControl (const CControl& c)
: CView (c)
, listener (c.listener)
, tag (c.tag)
, value (c.value)
{
impl = std::unique_ptr<Impl> (new Impl);
impl->oldValue = c.impl->oldValue;
impl->defaultValue = c.impl->defaultValue;
impl->vmin = c.impl->vmin;
impl->vmax = c.impl->vmax;
impl->wheelInc = c.impl->wheelInc;
registerViewEventListener (impl.get ());
}
//------------------------------------------------------------------------
CControl::~CControl () noexcept
{
unregisterViewEventListener (impl.get ());
}
//------------------------------------------------------------------------
void CControl::registerControlListener (IControlListener* subListener)
{
vstgui_assert (listener != subListener, "the subListener is already the main listener");
impl->subListeners.add (subListener);
}
//------------------------------------------------------------------------
void CControl::unregisterControlListener (IControlListener* subListener)
{
impl->subListeners.remove (subListener);
}
//------------------------------------------------------------------------
void CControl::setWheelInc (float val)
{
impl->wheelInc = val;
}
//------------------------------------------------------------------------
float CControl::getWheelInc () const
{
return impl->wheelInc;
}
//------------------------------------------------------------------------
void CControl::setMin (float val)
{
impl->vmin = val;
bounceValue ();
}
//------------------------------------------------------------------------
float CControl::getMin () const
{
return impl->vmin;
}
//------------------------------------------------------------------------
void CControl::setMax (float val)
{
impl->vmax = val;
bounceValue ();
}
//------------------------------------------------------------------------
float CControl::getMax () const
{
return impl->vmax;
}
//------------------------------------------------------------------------
void CControl::setOldValue (float val)
{
impl->oldValue = val;
}
//------------------------------------------------------------------------
float CControl::getOldValue (void) const
{
return impl->oldValue;
}
//------------------------------------------------------------------------
void CControl::setDefaultValue (float val)
{
impl->defaultValue = val;
}
//------------------------------------------------------------------------
float CControl::getDefaultValue (void) const
{
return impl->defaultValue;
}
//------------------------------------------------------------------------
void CControl::setTag (int32_t val)
{
if (listener)
listener->controlTagWillChange (this);
tag = val;
if (listener)
listener->controlTagDidChange (this);
}
//------------------------------------------------------------------------
bool CControl::isEditing () const
{
return impl->editing > 0;
}
//------------------------------------------------------------------------
void CControl::beginEdit ()
{
// begin of edit parameter
impl->editing++;
if (impl->editing == 1)
{
if (listener)
listener->controlBeginEdit (this);
impl->subListeners.forEach ([this] (IControlListener* l) { l->controlBeginEdit (this); });
if (getFrame ())
getFrame ()->beginEdit (tag);
}
#if VSTGUI_CCONTROL_LOG_EDITING
DebugPrint("beginEdit [%d] - %d\n", tag, impl->editing);
#endif
}
//------------------------------------------------------------------------
void CControl::endEdit ()
{
if (!isEditing ())
return;
--impl->editing;
if (impl->editing == 0)
{
if (getFrame ())
getFrame ()->endEdit (tag);
if (listener)
listener->controlEndEdit (this);
impl->subListeners.forEach ([this] (IControlListener* l) { l->controlEndEdit (this); });
}
#if VSTGUI_CCONTROL_LOG_EDITING
DebugPrint("endEdit [%d] - %d\n", tag, impl->editing);
#endif
}
//------------------------------------------------------------------------
void CControl::setValue (float val) { value = clamp (val, getMin (), getMax ()); }
//------------------------------------------------------------------------
void CControl::setValueNormalized (float val)
{
if (getRange () == 0.f)
{
value = getMin ();
return;
}
val = clampNorm (val);
setValue (normalizedToPlain (val, getMin (), getMax ()));
}
//------------------------------------------------------------------------
float CControl::getValueNormalized () const
{
auto range = getRange ();
if (range == 0.f)
return 0.f;
return plainToNormalized<float> (value, getMin (), getMax ());
}
//------------------------------------------------------------------------
void CControl::valueChanged ()
{
if (listener)
listener->valueChanged (this);
impl->subListeners.forEach ([this] (IControlListener* l) { l->valueChanged (this); });
}
//------------------------------------------------------------------------
bool CControl::isDirty () const
{
if (getOldValue () != value || CView::isDirty ())
return true;
return false;
}
//------------------------------------------------------------------------
void CControl::setDirty (bool val)
{
CView::setDirty (val);
if (val)
{
if (value != -1.f)
setOldValue (-1.f);
else
setOldValue (0.f);
}
else
setOldValue (value);
}
//------------------------------------------------------------------------
void CControl::bounceValue () { value = clamp (value, getMin (), getMax ()); }
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
CControl::CheckDefaultValueFuncT CControl::CheckDefaultValueFunc = [] (CControl*,
CButtonState button) {
#if TARGET_OS_IPHONE
return button.isDoubleClick ();
#else
return (button.isLeftButton () && button.getModifierState () == kDefaultValueModifier);
#endif // TARGET_OS_IPHONE
};
#endif // VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
CControl::CheckDefaultValueEventFuncT CControl::CheckDefaultValueEventFunc =
[] (CControl* c, MouseDownEvent& event) {
#if VSTGUI_ENABLE_DEPRECATED_METHODS
if (event.buttonState.isLeft ())
{
return CheckDefaultValueFunc (c, buttonStateFromMouseEvent (event));
}
return false;
#else
#if TARGET_OS_IPHONE
return event.buttonState.isLeft () && event.clickCount == 2;
#else
return event.buttonState.isLeft () && event.modifiers.is (ModifierKey::Control);
#endif // TARGET_OS_IPHONE
#endif // VSTGUI_ENABLE_DEPRECATED_METHODS
};
//------------------------------------------------------------------------
bool CControl::drawFocusOnTop ()
{
return false;
}
//------------------------------------------------------------------------
bool CControl::getFocusPath (CGraphicsPath& outPath)
{
if (wantsFocus ())
{
CCoord focusWidth = getFrame ()->getFocusWidth ();
CRect r (getVisibleViewSize ());
if (!r.isEmpty ())
{
outPath.addRect (r);
r.extend (focusWidth, focusWidth);
outPath.addRect (r);
}
}
return true;
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//-----------------------------------------------------------------------------
int32_t CControl::mapVstKeyModifier (int32_t vstModifier)
{
int32_t modifiers = 0;
if (vstModifier & MODIFIER_SHIFT)
modifiers |= kShift;
if (vstModifier & MODIFIER_ALTERNATE)
modifiers |= kAlt;
if (vstModifier & MODIFIER_COMMAND)
modifiers |= kApple;
if (vstModifier & MODIFIER_CONTROL)
modifiers |= kControl;
return modifiers;
}
#endif // VSTGUI_ENABLE_DEPRECATED_METHODS
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
void IMultiBitmapControl::autoComputeHeightOfOneImage ()
{
auto* view = dynamic_cast<CView*>(this);
if (view)
{
const CRect& viewSize = view->getViewSize ();
heightOfOneImage = viewSize.getHeight ();
}
}
#endif
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
void CMouseWheelEditingSupport::onMouseWheelEditing (CControl* control)
{
if (!control->isEditing ())
control->beginEdit ();
endEditTimer = makeOwned<CVSTGUITimer> (
[control] (CVSTGUITimer* timer) {
control->endEdit ();
timer->stop ();
},
500);
}
//------------------------------------------------------------------------
void CMouseWheelEditingSupport::invalidMouseWheelEditTimer (CControl* control)
{
if (endEditTimer)
endEditTimer = nullptr;
if (control->isEditing ())
control->endEdit ();
}
} // VSTGUI
@@ -0,0 +1,168 @@
// 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
#pragma once
#include "../cview.h"
#include "../ifocusdrawing.h"
namespace VSTGUI {
namespace Constants {
static constexpr auto pi = 3.14159265358979323846;
static constexpr auto double_pi = 6.28318530717958647692;
static constexpr auto half_pi = 1.57079632679489661923f;
static constexpr auto quarter_pi = 0.78539816339744830962;
static constexpr auto e = 2.7182818284590452354;
static constexpr auto ln2 = 0.69314718055994530942;
static constexpr auto sqrt2 = 1.41421356237309504880;
} // Constants
//-----------------------------------------------------------------------------
// CControl Declaration
//! @brief base class of all VSTGUI controls
//-----------------------------------------------------------------------------
class CControl : public CView, public IFocusDrawing
{
public:
CControl (const CRect& size, IControlListener* listener = nullptr, int32_t tag = 0, CBitmap* pBackground = nullptr);
CControl (const CControl& c);
//-----------------------------------------------------------------------------
/// @name Value Methods
//-----------------------------------------------------------------------------
//@{
virtual void setValue (float val);
virtual float getValue () const { return value; }
virtual void setValueNormalized (float val);
virtual float getValueNormalized () const;
virtual void setMin (float val);
virtual float getMin () const;
virtual void setMax (float val);
virtual float getMax () const;
float getRange () const { return getMax () - getMin (); }
virtual void setOldValue (float val);
virtual float getOldValue () const;
virtual void setDefaultValue (float val);
virtual float getDefaultValue () const;
virtual void bounceValue ();
/** notifies listener and dependent objects */
virtual void valueChanged ();
//@}
//-----------------------------------------------------------------------------
/// @name Editing Methods
//-----------------------------------------------------------------------------
//@{
virtual void setTag (int32_t val);
virtual int32_t getTag () const { return tag; }
virtual void beginEdit ();
virtual void endEdit ();
bool isEditing () const;
/** get main listener */
virtual IControlListener* getListener () const { return listener; }
/** set main listener */
virtual void setListener (IControlListener* l) { listener = l; }
/** register a sub listener */
void registerControlListener (IControlListener* listener);
/** unregister a sub listener */
void unregisterControlListener (IControlListener* listener);
//@}
//-----------------------------------------------------------------------------
/// @name Misc
//-----------------------------------------------------------------------------
//@{
virtual void setWheelInc (float val);
virtual float getWheelInc () const;
//@}
// overrides
void draw (CDrawContext* pContext) override = 0;
bool isDirty () const override;
void setDirty (bool val = true) override;
bool drawFocusOnTop () override;
bool getFocusPath (CGraphicsPath& outPath) override;
using CheckDefaultValueEventFuncT = bool (*) (CControl*, MouseDownEvent&);
/** Function to check if a mouse down event should reset the value to its default value for a
*control. Per default this checks for a left mouse down button and the control modifier key. */
static CheckDefaultValueEventFuncT CheckDefaultValueEventFunc;
/** zoom modifier key, per default is the shift key */
inline static int32_t kZoomModifier = kShift;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
/** \deprecated default value modifier key, per default is the control key */
inline static int32_t kDefaultValueModifier = kControl;
using CheckDefaultValueFuncT = bool (*) (CControl*, CButtonState);
/** \deprecated Function to check if the button state is the state to set the control value to
* its default value. The default implementation uses the kDefaultValueModifier (see above). Use
* this to change this to double click per example. But consider to change this to the same
* behaviour as the host you are running in for best user experience. */
static CheckDefaultValueFuncT CheckDefaultValueFunc;
#endif
CLASS_METHODS_VIRTUAL(CControl, CView)
protected:
~CControl () noexcept override;
VSTGUI_DEPRECATED (static int32_t mapVstKeyModifier (int32_t vstModifier);)
IControlListener* listener;
int32_t tag;
float value;
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//-----------------------------------------------------------------------------
// IMultiBitmapControl Declaration
//! @brief interface for controls with sub images
//-----------------------------------------------------------------------------
class IMultiBitmapControl
{
public:
virtual ~IMultiBitmapControl() {}
virtual void setHeightOfOneImage (const CCoord& height) { heightOfOneImage = height; }
virtual CCoord getHeightOfOneImage () const { return heightOfOneImage; }
virtual void setNumSubPixmaps (int32_t numSubPixmaps) { subPixmaps = numSubPixmaps; }
virtual int32_t getNumSubPixmaps () const { return subPixmaps; }
virtual void autoComputeHeightOfOneImage ();
protected:
IMultiBitmapControl () : heightOfOneImage (0), subPixmaps (0) {}
CCoord heightOfOneImage;
int32_t subPixmaps;
};
#endif
//-----------------------------------------------------------------------------
// CMouseWheelEditingSupport Declaration
//! @brief Helper class for mouse wheel editing
//-----------------------------------------------------------------------------
class CMouseWheelEditingSupport
{
protected:
void invalidMouseWheelEditTimer (CControl* control);
void onMouseWheelEditing (CControl* control);
private:
SharedPointer<CBaseObject> endEditTimer {nullptr};
};
} // VSTGUI
@@ -0,0 +1,302 @@
// 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 "cfontchooser.h"
#include "../cdatabrowser.h"
#include "../cdrawcontext.h"
#include "cbuttons.h"
#include "ctextedit.h"
#include "cscrollbar.h"
#include "../cstring.h"
#include "../platform/platformfactory.h"
#include "../platform/iplatformfont.h"
#include <list>
#include <cmath>
namespace VSTGUI {
/// @cond ignore
namespace CFontChooserInternal {
class FontPreviewView : public CView
{
public:
FontPreviewView (const CRect& size, const CColor& color = kWhiteCColor) : CView (size), font (nullptr), fontColor (color) {}
~FontPreviewView () noexcept override { if (font) font->forget (); }
void setFont (CFontRef newFont)
{
if (font)
font->forget ();
font = newFont;
if (font)
font->remember ();
invalid ();
}
void draw (CDrawContext *context) override
{
context->setFontColor (fontColor);
context->setFont (font);
std::string text;
char string[2];
CRect glyphRect (getViewSize ().left, getViewSize ().top, getViewSize ().left, getViewSize ().top);
CCoord height = ceil (font->getPlatformFont ()->getAscent () + font->getPlatformFont ()->getDescent () + font->getPlatformFont ()->getLeading () + 2.);
glyphRect.setHeight (height);
for (int8_t i = 33; i < 126;)
{
while (glyphRect.right < getViewSize ().right && i < 126)
{
snprintf (string, 2, "%c", i++);
text += string;
glyphRect.setWidth (context->getStringWidth (text.c_str ()));
}
context->drawString (text.c_str (), glyphRect, kLeftText);
glyphRect.left = glyphRect.right = getViewSize ().left;
glyphRect.offset (0, height);
text = "";
}
setDirty (false);
}
protected:
CFontRef font;
CColor fontColor;
};
enum {
kFontChooserSizeTag,
kFontChooserBoldTag,
kFontChooserItalicTag,
kFontChooserUnderlineTag,
kFontChooserStrikeoutTag
};
} // CFontChooserInternal
/// @endcond
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CFontChooser::CFontChooser (IFontChooserDelegate* delegate, CFontRef initialFont, const CFontChooserUIDefinition& uiDef)
: CViewContainer (CRect (0, 0, 300, 500))
, delegate (nullptr)
, fontBrowser (nullptr)
, selFont (nullptr)
{
std::list<std::string> fnList;
getPlatformFactory ().getAllFontFamilies ([&fnList] (const std::string& name) {
fnList.push_back (name);
return true;
});
fnList.sort ();
std::list<std::string>::const_iterator it = fnList.begin ();
while (it != fnList.end ())
{
fontNames.emplace_back (*it);
++it;
}
auto* dbSource = new GenericStringListDataBrowserSource (&fontNames, this);
dbSource->setupUI (uiDef.selectionColor, uiDef.fontColor, uiDef.rowlineColor, uiDef.rowBackColor, uiDef.rowAlternateBackColor, uiDef.font, uiDef.rowHeight);
int32_t dbStyle = CDataBrowser::kDrawRowLines | CScrollView::kVerticalScrollbar | CScrollView::kDontDrawFrame | CScrollView::kOverlayScrollbars;
fontBrowser = new CDataBrowser (CRect (0, 0, 200, 500), dbSource, dbStyle, uiDef.scrollbarWidth);
dbSource->forget ();
fontBrowser->setAutosizeFlags (kAutosizeLeft | kAutosizeTop | kAutosizeBottom);
fontBrowser->setTransparency (true);
CScrollbar* scrollbar = fontBrowser->getVerticalScrollbar ();
if (scrollbar)
{
scrollbar->setBackgroundColor (uiDef.scrollbarBackgroundColor);
scrollbar->setFrameColor (uiDef.scrollbarFrameColor);
scrollbar->setScrollerColor (uiDef.scrollbarScrollerColor);
}
addView (fontBrowser);
CRect controlRect (210, 0, 300, 20);
auto* label = new CTextLabel (controlRect, "Size:");
label->setFont (uiDef.font);
label->setFontColor (uiDef.fontColor);
label->sizeToFit ();
label->setHoriAlign (kLeftText);
label->setTransparency (true);
label->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
addView (label);
CRect teRect = label->getViewSize ();
teRect.left = teRect.right + 5.;
teRect.right = controlRect.right;
sizeEdit = new CTextEdit (teRect, this, CFontChooserInternal::kFontChooserSizeTag);
sizeEdit->setFont (uiDef.font);
sizeEdit->setFontColor (uiDef.fontColor);
sizeEdit->setHoriAlign (kLeftText);
sizeEdit->setTransparency (true);
sizeEdit->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
sizeEdit->setMax (2000);
sizeEdit->setMin (6);
sizeEdit->setValue (2000);
sizeEdit->sizeToFit ();
sizeEdit->setStringToValueFunction ([] (UTF8StringPtr txt, float& result, CTextEdit* textEdit) { result = UTF8StringView (txt).toFloat (); return true; });
addView (sizeEdit);
controlRect.offset (0, 20);
boldBox = new CCheckBox (controlRect, this, CFontChooserInternal::kFontChooserBoldTag, "Bold");
boldBox->setFont (uiDef.font);
boldBox->setFontColor (uiDef.fontColor);
boldBox->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
boldBox->sizeToFit ();
addView (boldBox);
controlRect.offset (0, 20);
italicBox = new CCheckBox (controlRect, this, CFontChooserInternal::kFontChooserItalicTag, "Italic");
italicBox->setFont (uiDef.font);
italicBox->setFontColor (uiDef.fontColor);
italicBox->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
italicBox->sizeToFit ();
addView (italicBox);
controlRect.offset (0, 20);
underlineBox = new CCheckBox (controlRect, this, CFontChooserInternal::kFontChooserUnderlineTag, "Underline");
underlineBox->setFont (uiDef.font);
underlineBox->setFontColor (uiDef.fontColor);
underlineBox->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
underlineBox->sizeToFit ();
addView (underlineBox);
controlRect.offset (0, 20);
strikeoutBox = new CCheckBox (controlRect, this, CFontChooserInternal::kFontChooserStrikeoutTag, "Strikeout");
strikeoutBox->setFont (uiDef.font);
strikeoutBox->setFontColor (uiDef.fontColor);
strikeoutBox->setAutosizeFlags (kAutosizeLeft | kAutosizeTop);
strikeoutBox->sizeToFit ();
addView (strikeoutBox);
CViewContainer* container = new CViewContainer (CRect (controlRect.left, controlRect.bottom+10, 300, 500));
container->setBackgroundColor (uiDef.previewBackgroundColor);
container->setAutosizeFlags (kAutosizeTop | kAutosizeBottom | kAutosizeLeft | kAutosizeRight);
fontPreviewView = new CFontChooserInternal::FontPreviewView (CRect (10, 10, container->getWidth () - 10, container->getHeight () - 10), uiDef.previewTextColor);
fontPreviewView->setAutosizeFlags (kAutosizeAll);
container->addView (fontPreviewView);
addView (container);
setFont (initialFont ? initialFont : kSystemFont);
sizeToFit ();
this->delegate = delegate;
}
//-----------------------------------------------------------------------------
CFontChooser::~CFontChooser () noexcept
{
if (selFont)
selFont->forget ();
}
//-----------------------------------------------------------------------------
void CFontChooser::setFont (CFontRef font)
{
if (font)
{
if (selFont)
selFont->forget ();
selFont = new CFontDesc (*font);
sizeEdit->setValue ((float)font->getSize ());
boldBox->setValue ((font->getStyle () & kBoldFace) ? 1.f : 0.f);
italicBox->setValue ((font->getStyle () & kItalicFace) ? 1.f : 0.f);
underlineBox->setValue ((font->getStyle () & kUnderlineFace) ? 1.f : 0.f);
strikeoutBox->setValue ((font->getStyle () & kStrikethroughFace) ? 1.f : 0.f);
auto it = fontNames.begin ();
int32_t row = 0;
while (it != fontNames.end ())
{
if (*it == font->getName ())
{
fontBrowser->setSelectedRow (row, true);
break;
}
++it;
row++;
}
static_cast<CFontChooserInternal::FontPreviewView*> (fontPreviewView)->setFont (selFont);
}
invalid ();
}
//-----------------------------------------------------------------------------
void CFontChooser::valueChanged (CControl* pControl)
{
if (selFont == nullptr)
return;
switch (pControl->getTag ())
{
case CFontChooserInternal::kFontChooserSizeTag:
{
pControl->setValue (pControl->getValue ());
selFont->setSize (pControl->getValue ());
break;
}
case CFontChooserInternal::kFontChooserBoldTag:
{
if (pControl->getValue () == 1)
selFont->setStyle (selFont->getStyle () | kBoldFace);
else
selFont->setStyle (selFont->getStyle () & ~kBoldFace);
break;
}
case CFontChooserInternal::kFontChooserItalicTag:
{
if (pControl->getValue () == 1)
selFont->setStyle (selFont->getStyle () | kItalicFace);
else
selFont->setStyle (selFont->getStyle () & ~kItalicFace);
break;
}
case CFontChooserInternal::kFontChooserUnderlineTag:
{
if (pControl->getValue () == 1)
selFont->setStyle (selFont->getStyle () | kUnderlineFace);
else
selFont->setStyle (selFont->getStyle () & ~kUnderlineFace);
break;
}
case CFontChooserInternal::kFontChooserStrikeoutTag:
{
if (pControl->getValue () == 1)
selFont->setStyle (selFont->getStyle () | kStrikethroughFace);
else
selFont->setStyle (selFont->getStyle () & ~kStrikethroughFace);
break;
}
}
if (delegate)
delegate->fontChanged (this, selFont);
static_cast<CFontChooserInternal::FontPreviewView*> (fontPreviewView)->setFont (selFont);
}
//-----------------------------------------------------------------------------
void CFontChooser::dbSelectionChanged (int32_t selectedRow, GenericStringListDataBrowserSource* source)
{
if (selectedRow >= 0 && static_cast<size_t> (selectedRow) <= fontNames.size ())
selFont->setName (fontNames[static_cast<size_t> (selectedRow)].data ());
static_cast<CFontChooserInternal::FontPreviewView*> (fontPreviewView)->setFont (selFont);
if (delegate)
delegate->fontChanged (this, selFont);
}
//-----------------------------------------------------------------------------
bool CFontChooser::attached (CView* parent)
{
if (CViewContainer::attached (parent))
{
fontBrowser->makeRowVisible (fontBrowser->getSelectedRow ());
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void CFontChooser::onKeyboardEvent (KeyboardEvent& event)
{
fontBrowser->onKeyboardEvent (event);
}
} // VSTGUI
@@ -0,0 +1,92 @@
// 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
#pragma once
#include "../vstguifwd.h"
#include "../cviewcontainer.h"
#include "../cfont.h"
#include "../cdatabrowser.h"
#include "../genericstringlistdatabrowsersource.h"
#include "icontrollistener.h"
namespace VSTGUI {
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class IFontChooserDelegate
{
public:
virtual void fontChanged (CFontChooser* chooser, CFontRef newFont) = 0;
};
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
struct CFontChooserUIDefinition
{
CFontRef font;
int32_t rowHeight;
CColor fontColor;
CColor selectionColor;
CColor rowlineColor;
CColor rowBackColor;
CColor rowAlternateBackColor;
CColor previewTextColor;
CColor previewBackgroundColor;
CColor scrollbarScrollerColor;
CColor scrollbarFrameColor;
CColor scrollbarBackgroundColor;
CCoord scrollbarWidth;
CFontChooserUIDefinition (CFontRef font = kSystemFont,
const CColor& fontColor = kWhiteCColor,
const CColor& selectionColor = kBlueCColor,
const CColor& rowlineColor = kGreyCColor,
const CColor& rowBackColor = kTransparentCColor,
const CColor& rowAlternateBackColor = kTransparentCColor,
const CColor& previewTextColor = kBlackCColor,
const CColor& previewBackgroundColor = kWhiteCColor,
const CColor& scrollbarScrollerColor = kBlueCColor,
const CColor& scrollbarFrameColor = kBlackCColor,
const CColor& scrollbarBackgroundColor = kGreyCColor,
int32_t rowHeight = -1,
CCoord scrollbarWidth = 16)
: font (font), rowHeight (rowHeight), fontColor (fontColor), selectionColor (selectionColor), rowlineColor (rowlineColor)
, rowBackColor (rowBackColor), rowAlternateBackColor (rowAlternateBackColor), previewTextColor (previewTextColor), previewBackgroundColor (previewBackgroundColor)
, scrollbarScrollerColor (scrollbarScrollerColor), scrollbarFrameColor (scrollbarFrameColor)
, scrollbarBackgroundColor (scrollbarBackgroundColor), scrollbarWidth (scrollbarWidth)
{}
};
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class CFontChooser : public CViewContainer, public IControlListener, public GenericStringListDataBrowserSourceSelectionChanged
{
public:
CFontChooser (IFontChooserDelegate* delegate, CFontRef initialFont = nullptr, const CFontChooserUIDefinition& uiDef = CFontChooserUIDefinition ());
~CFontChooser () noexcept override;
void setFont (CFontRef font);
protected:
void dbSelectionChanged (int32_t selectedRow, GenericStringListDataBrowserSource* source) override;
void valueChanged (CControl* pControl) override;
bool attached (CView* parent) override;
void onKeyboardEvent (KeyboardEvent& event) override;
IFontChooserDelegate* delegate;
CDataBrowser* fontBrowser;
CTextEdit* sizeEdit;
CCheckBox* boldBox;
CCheckBox* italicBox;
CCheckBox* underlineBox;
CCheckBox* strikeoutBox;
CView* fontPreviewView;
GenericStringListDataBrowserSource::StringVector fontNames;
CFontRef selFont;
};
} // VSTGUI
@@ -0,0 +1,911 @@
// 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 "cknob.h"
#include "../cbitmap.h"
#include "../cdrawcontext.h"
#include "../cframe.h"
#include "../cgraphicspath.h"
#include "../cvstguitimer.h"
#include "../events.h"
#include <cmath>
namespace VSTGUI {
#if TARGET_OS_IPHONE
static const float kCKnobRangeDefault = 300.f;
#else
static const float kCKnobRangeDefault = 200.f;
#endif
static constexpr CViewAttributeID kCKnobMouseStateAttribute = 'knms';
//------------------------------------------------------------------------
struct CKnobBase::MouseEditingState
{
CPoint firstPoint;
CPoint lastPoint;
float startValue;
float entryState;
float range;
float coef;
CButtonState oldButton;
bool modeLinear;
};
//------------------------------------------------------------------------
CKnobBase::CKnobBase (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background)
: CControl (size, listener, tag, background)
{
rangeAngle = 1.f;
setStartAngle ((float)(3.f * Constants::quarter_pi));
setRangeAngle ((float)(3.f * Constants::half_pi));
zoomFactor = 1.5f;
knobRange = kCKnobRangeDefault;
}
//------------------------------------------------------------------------
CKnobBase::CKnobBase (const CKnobBase& k)
: CControl (k)
, startAngle (k.startAngle)
, rangeAngle (k.rangeAngle)
, zoomFactor (k.zoomFactor)
, inset (k.inset)
{
}
//------------------------------------------------------------------------
void CKnobBase::setViewSize (const CRect &rect, bool invalid)
{
CControl::setViewSize (rect, invalid);
compute ();
}
//------------------------------------------------------------------------
bool CKnobBase::sizeToFit ()
{
if (getDrawBackground ())
{
CRect vs (getViewSize ());
vs.setWidth (getDrawBackground ()->getWidth ());
vs.setHeight (getDrawBackground ()->getHeight ());
setViewSize (vs);
setMouseableArea (vs);
return true;
}
return false;
}
//------------------------------------------------------------------------
auto CKnobBase::getMouseEditingState () -> MouseEditingState&
{
MouseEditingState* state = nullptr;
if (!getAttribute (kCKnobMouseStateAttribute, state))
{
state = new MouseEditingState;
setAttribute (kCKnobMouseStateAttribute, state);
}
return *state;
}
//------------------------------------------------------------------------
void CKnobBase::clearMouseEditingState ()
{
MouseEditingState* state = nullptr;
if (!getAttribute (kCKnobMouseStateAttribute, state))
return;
delete state;
removeAttribute (kCKnobMouseStateAttribute);
}
//------------------------------------------------------------------------
CMouseEventResult CKnobBase::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (!buttons.isLeftButton ())
return kMouseEventNotHandled;
invalidMouseWheelEditTimer (this);
beginEdit ();
auto& mouseState = getMouseEditingState ();
mouseState.firstPoint = where;
mouseState.lastPoint (-1, -1);
mouseState.startValue = getOldValue ();
mouseState.modeLinear = false;
mouseState.entryState = value;
mouseState.range = knobRange;
mouseState.coef = (getMax () - getMin ()) / mouseState.range;
mouseState.oldButton = buttons;
int32_t mode = kCircularMode;
int32_t newMode = getFrame ()->getKnobMode ();
if (kLinearMode == newMode)
{
if (!(buttons & kAlt))
mode = newMode;
}
else if (buttons & kAlt)
{
mode = kLinearMode;
}
if (mode == kLinearMode)
{
if (buttons & kZoomModifier)
mouseState.range *= zoomFactor;
mouseState.lastPoint = where;
mouseState.modeLinear = true;
mouseState.coef = (getMax () - getMin ()) / mouseState.range;
}
else
{
CPoint where2 (where);
where2.offset (-getViewSize ().left, -getViewSize ().top);
mouseState.startValue = valueFromPoint (where2);
mouseState.lastPoint = where;
}
return onMouseMoved (where, buttons);
}
//------------------------------------------------------------------------
CMouseEventResult CKnobBase::onMouseUp (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
endEdit ();
clearMouseEditingState ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CKnobBase::onMouseCancel ()
{
if (isEditing ())
{
auto& mouseState = getMouseEditingState ();
value = mouseState.startValue;
if (isDirty ())
{
valueChanged ();
invalid ();
}
endEdit ();
clearMouseEditingState ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CKnobBase::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
if (buttons.isLeftButton () && isEditing ())
{
auto& mouseState = getMouseEditingState ();
float middle = (getMax () - getMin ()) * 0.5f;
if (where != mouseState.lastPoint)
{
mouseState.lastPoint = where;
if (mouseState.modeLinear)
{
CCoord diff = (mouseState.firstPoint.y - where.y) + (where.x - mouseState.firstPoint.x);
if (buttons != mouseState.oldButton)
{
mouseState.range = knobRange;
if (buttons & kZoomModifier)
mouseState.range *= zoomFactor;
float coef2 = (getMax () - getMin ()) / mouseState.range;
mouseState.entryState += (float)(diff * (mouseState.coef - coef2));
mouseState.coef = coef2;
mouseState.oldButton = buttons;
}
value = (float)(mouseState.entryState + diff * mouseState.coef);
bounceValue ();
}
else
{
where.offset (-getViewSize ().left, -getViewSize ().top);
value = valueFromPoint (where);
if (mouseState.startValue - value > middle)
value = getMax ();
else if (value - mouseState.startValue > middle)
value = getMin ();
else
mouseState.startValue = value;
}
if (value != getOldValue ())
valueChanged ();
if (isDirty ())
invalid ();
}
return kMouseEventHandled;
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
void CKnobBase::onMouseWheelEvent (MouseWheelEvent& event)
{
onMouseWheelEditing (this);
float v = getValueNormalized ();
if (buttonStateFromEventModifiers (event.modifiers) & kZoomModifier)
v += 0.1f * static_cast<float> (event.deltaY) * getWheelInc ();
else
v += static_cast<float> (event.deltaY) * getWheelInc ();
setValueNormalized (v);
if (isDirty ())
{
invalid ();
valueChanged ();
}
event.consumed = true;
}
//------------------------------------------------------------------------
void CKnobBase::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type != EventType::KeyDown)
return;
switch (event.virt)
{
case VirtualKey::Up :
case VirtualKey::Right :
case VirtualKey::Down :
case VirtualKey::Left :
{
float distance = 1.f;
if (event.virt == VirtualKey::Down || event.virt == VirtualKey::Left)
distance = -distance;
float v = getValueNormalized ();
if (buttonStateFromEventModifiers (event.modifiers) & kZoomModifier)
v += 0.1f * distance * getWheelInc ();
else
v += distance * getWheelInc ();
setValueNormalized (v);
if (isDirty ())
{
invalid ();
beginEdit ();
valueChanged ();
endEdit ();
}
event.consumed = true;
}
case VirtualKey::Escape:
{
if (isEditing ())
{
onMouseCancel ();
event.consumed = true;
}
break;
}
default: return;
}
}
//------------------------------------------------------------------------
void CKnobBase::setStartAngle (float val)
{
startAngle = val;
compute ();
}
//------------------------------------------------------------------------
void CKnobBase::setRangeAngle (float val)
{
rangeAngle = val;
compute ();
}
//------------------------------------------------------------------------
void CKnobBase::compute ()
{
setDirty ();
}
//------------------------------------------------------------------------
void CKnobBase::valueToPoint (CPoint &point) const
{
float alpha = (value - getMin()) / (getMax() - getMin());
alpha = startAngle + alpha*rangeAngle;
CPoint c (getViewSize ().getWidth () / 2., getViewSize ().getHeight () / 2.);
double xradius = c.x - inset;
double yradius = c.y - inset;
point.x = (CCoord)(c.x + cosf (alpha) * xradius + 0.5f);
point.y = (CCoord)(c.y + sinf (alpha) * yradius + 0.5f);
}
//------------------------------------------------------------------------
float CKnobBase::valueFromPoint (CPoint &point) const
{
float v;
double d = rangeAngle * 0.5;
double a = startAngle + d;
CPoint c (getViewSize ().getWidth () / 2., getViewSize ().getHeight () / 2.);
double xradius = c.x - inset;
double yradius = c.y - inset;
double dx = (point.x - c.x) / xradius;
double dy = (point.y - c.y) / yradius;
double alpha = atan2 (dy, dx) - a;
while (alpha >= Constants::pi)
alpha -= Constants::double_pi;
while (alpha < -Constants::pi)
alpha += Constants::double_pi;
if (d < 0.0)
alpha = -alpha;
if (alpha > d)
v = getMax ();
else if (alpha < -d)
v = getMin ();
else
{
v = float (0.5 + alpha / rangeAngle);
v = getMin () + (v * getRange ());
}
return v;
}
//------------------------------------------------------------------------
void CKnobBase::setMin (float val)
{
CControl::setMin (val);
if (getValue () < val)
setValue (val);
compute ();
}
//------------------------------------------------------------------------
void CKnobBase::setMax (float val)
{
CControl::setMax (val);
if (getValue () > val)
setValue (val);
compute ();
}
//------------------------------------------------------------------------
// CKnob
//------------------------------------------------------------------------
/*! @class CKnob
Define a knob with a given background and foreground handle.
The handle describes a circle over the background (between -45deg and +225deg).
By clicking alt modifier and left mouse button the default value is used.
By clicking alt modifier and left mouse button the value changes with a vertical move (version 2.1)
*/
//------------------------------------------------------------------------
/**
* CKnob constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background background bitmap
* @param handle handle bitmap
* @param offset offset of background bitmap
* @param drawStyle draw style
*/
//------------------------------------------------------------------------
CKnob::CKnob (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, CBitmap* handle, const CPoint& offset, int32_t drawStyle)
: CKnobBase (size, listener, tag, background)
, offset (offset)
, drawStyle (drawStyle)
, handleLineWidth (1.)
, coronaInset (0)
, coronaOutlineWidthAdd (2.)
, pHandle (handle)
{
if (pHandle)
{
pHandle->remember ();
inset = (CCoord)((float)pHandle->getWidth () / 2.f + 2.5f);
}
else
{
inset = 3;
}
colorShadowHandle = kGreyCColor;
colorHandle = kWhiteCColor;
coronaLineStyle = kLineOnOffDash;
coronaLineStyle.getDashLengths ()[1] = 2.;
setWantsFocus (true);
}
//------------------------------------------------------------------------
CKnob::CKnob (const CKnob& v)
: CKnobBase (v)
, offset (v.offset)
, drawStyle (v.drawStyle)
, colorHandle (v.colorHandle)
, colorShadowHandle (v.colorShadowHandle)
, handleLineWidth (v.handleLineWidth)
, coronaInset (v.coronaInset)
, coronaOutlineWidthAdd (v.coronaInset)
, coronaLineStyle (v.coronaLineStyle)
, pHandle (v.pHandle)
{
if (pHandle)
pHandle->remember ();
}
//------------------------------------------------------------------------
CKnob::~CKnob () noexcept
{
if (pHandle)
pHandle->forget ();
}
//------------------------------------------------------------------------
bool CKnob::drawFocusOnTop ()
{
if (drawStyle & kCoronaDrawing && wantsFocus ())
{
return false;
}
return CKnobBase::drawFocusOnTop ();
}
//------------------------------------------------------------------------
bool CKnob::getFocusPath (CGraphicsPath &outPath)
{
if (drawStyle & kCoronaDrawing && wantsFocus ())
{
CRect corona (getViewSize ());
corona.inset (coronaInset, coronaInset);
corona.inset (handleLineWidth/2., handleLineWidth/2.);
outPath.addEllipse (corona);
return true;
}
return CKnobBase::getFocusPath (outPath);
}
//------------------------------------------------------------------------
void CKnob::draw (CDrawContext *pContext)
{
if (getDrawBackground ())
{
getDrawBackground ()->draw (pContext, getViewSize (), offset);
}
if (pHandle)
drawHandle (pContext);
else
{
if (drawStyle & kCoronaOutline)
drawCoronaOutline (pContext);
if (drawStyle & kCoronaDrawing)
drawCorona (pContext);
if (!(drawStyle & kSkipHandleDrawing))
{
if (drawStyle & kHandleCircleDrawing)
drawHandleAsCircle (pContext);
else
drawHandleAsLine (pContext);
}
}
setDirty (false);
}
//------------------------------------------------------------------------
void CKnob::addArc (CGraphicsPath* path, const CRect& r, double startAngle, double sweepAngle)
{
CCoord w = r.getWidth ();
CCoord h = r.getHeight ();
double endAngle = startAngle + sweepAngle;
if (w != h)
{
startAngle = atan2 (sin (startAngle) * h, cos (startAngle) * w);
endAngle = atan2 (sin (endAngle) * h, cos (endAngle) * w);
}
path->addArc (r, startAngle / Constants::pi * 180, endAngle / Constants::pi * 180, sweepAngle >= 0);
}
//------------------------------------------------------------------------
void CKnob::drawCoronaOutline (CDrawContext* pContext) const
{
auto path = owned (pContext->createGraphicsPath ());
if (path == nullptr)
return;
CRect corona (getViewSize ());
corona.inset (coronaInset, coronaInset);
auto start = startAngle;
auto range = rangeAngle;
if (coronaOutlineWidthAdd && (drawStyle & kCoronaLineCapButt))
{
auto a = static_cast<float> (coronaOutlineWidthAdd / getWidth ());
start -= a;
range += a * 2.f;
}
addArc (path, corona, start, range);
pContext->setFrameColor (colorShadowHandle);
CLineStyle lineStyle (kLineSolid);
if (!(drawStyle & kCoronaLineCapButt))
lineStyle.setLineCap (CLineStyle::kLineCapRound);
pContext->setLineStyle (lineStyle);
pContext->setLineWidth (handleLineWidth+coronaOutlineWidthAdd);
pContext->setDrawMode (kAntiAliasing | kNonIntegralMode);
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
//------------------------------------------------------------------------
void CKnob::drawCorona (CDrawContext* pContext) const
{
auto path = owned (pContext->createGraphicsPath ());
if (path == nullptr)
return;
float coronaValue = getValueNormalized ();
if (drawStyle & kCoronaInverted)
coronaValue = 1.f - coronaValue;
CRect corona (getViewSize ());
corona.inset (coronaInset, coronaInset);
if (drawStyle & kCoronaFromCenter)
addArc (path, corona, 1.5 * Constants::pi, rangeAngle * (coronaValue - 0.5));
else
{
if (drawStyle & kCoronaInverted)
addArc (path, corona, startAngle + rangeAngle, -rangeAngle * coronaValue);
else
addArc (path, corona, startAngle, rangeAngle * coronaValue);
}
pContext->setFrameColor (coronaColor);
if (!(drawStyle & kCoronaLineCapButt))
{
CLineStyle lineStyle (kLineSolid);
lineStyle.setLineCap (CLineStyle::kLineCapRound);
pContext->setLineStyle (lineStyle);
}
else if (drawStyle & kCoronaLineDashDot)
pContext->setLineStyle (coronaLineStyle);
else
pContext->setLineStyle (kLineSolid);
pContext->setLineWidth (handleLineWidth);
pContext->setDrawMode (kAntiAliasing | kNonIntegralMode);
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
//------------------------------------------------------------------------
void CKnob::drawHandleAsCircle (CDrawContext* pContext) const
{
CPoint where;
valueToPoint (where);
where.offset (getViewSize ().left, getViewSize ().top);
CRect r (where.x - 0.5, where.y - 0.5, where.x + 0.5, where.y + 0.5);
r.extend (handleLineWidth, handleLineWidth);
pContext->setDrawMode (kAntiAliasing);
pContext->setFrameColor (colorShadowHandle);
pContext->setFillColor (colorHandle);
pContext->setLineWidth (0.5);
pContext->setLineStyle (kLineSolid);
pContext->setDrawMode (kAntiAliasing | kNonIntegralMode);
pContext->drawEllipse (r, kDrawFilledAndStroked);
}
//------------------------------------------------------------------------
void CKnob::drawHandleAsLine (CDrawContext* pContext) const
{
CPoint where;
valueToPoint (where);
CPoint origin (getViewSize ().getWidth () / 2, getViewSize ().getHeight () / 2);
where.offset (getViewSize ().left - 1, getViewSize ().top);
origin.offset (getViewSize ().left - 1, getViewSize ().top);
pContext->setFrameColor (colorShadowHandle);
pContext->setLineWidth (handleLineWidth);
pContext->setLineStyle (CLineStyle (CLineStyle::kLineCapRound));
pContext->setDrawMode (kAntiAliasing | kNonIntegralMode);
pContext->drawLine (where, origin);
where.offset (1, -1);
origin.offset (1, -1);
pContext->setFrameColor (colorHandle);
pContext->drawLine (where, origin);
}
//------------------------------------------------------------------------
void CKnob::drawHandle (CDrawContext *pContext)
{
CPoint where;
valueToPoint (where);
CCoord width = pHandle->getWidth ();
CCoord height = pHandle->getHeight ();
where.offset (getViewSize ().left - width / 2, getViewSize ().top - height / 2);
where.x = floor (where.x);
where.y = floor (where.y);
CRect handleSize (0, 0, width, height);
handleSize.offset (where.x, where.y);
pHandle->draw (pContext, handleSize);
}
//------------------------------------------------------------------------
void CKnob::setCoronaInset (CCoord inset)
{
if (inset != coronaInset)
{
coronaInset = inset;
setDirty ();
}
}
//------------------------------------------------------------------------
void CKnob::setCoronaColor (CColor color)
{
if (color != coronaColor)
{
coronaColor = color;
setDirty ();
}
}
//------------------------------------------------------------------------
void CKnob::setColorShadowHandle (CColor color)
{
if (color != colorShadowHandle)
{
colorShadowHandle = color;
setDirty ();
}
}
//------------------------------------------------------------------------
void CKnob::setColorHandle (CColor color)
{
if (color != colorHandle)
{
colorHandle = color;
setDirty ();
}
}
//------------------------------------------------------------------------
void CKnob::setHandleLineWidth (CCoord width)
{
if (width != handleLineWidth)
{
handleLineWidth = width;
setDirty ();
}
}
//------------------------------------------------------------------------
void CKnob::setCoronaOutlineWidthAdd (CCoord width)
{
if (width != coronaOutlineWidthAdd)
{
coronaOutlineWidthAdd = width;
setDirty ();
}
}
//------------------------------------------------------------------------
const CLineStyle::CoordVector& CKnob::getCoronaDashDotLengths () const
{
return coronaLineStyle.getDashLengths ();
}
//------------------------------------------------------------------------
void CKnob::setCoronaDashDotLengths (const CLineStyle::CoordVector& lengths)
{
if (coronaLineStyle.getDashLengths () != lengths)
{
coronaLineStyle.getDashLengths () = lengths;
setDirty ();
}
}
//------------------------------------------------------------------------
void CKnob::setDrawStyle (int32_t style)
{
if (style != drawStyle)
{
drawStyle = style;
setDirty ();
}
}
//------------------------------------------------------------------------
void CKnob::setHandleBitmap (CBitmap* bitmap)
{
if (pHandle)
{
pHandle->forget ();
pHandle = nullptr;
}
if (bitmap)
{
pHandle = bitmap;
pHandle->remember ();
inset = (CCoord)((float)pHandle->getWidth () / 2.f + 2.5f);
}
setDirty ();
}
//------------------------------------------------------------------------
// CAnimKnob
//------------------------------------------------------------------------
/*! @class CAnimKnob
Such as a CKnob control object, but there is a unique bitmap which contains different views
(subbitmaps) of this knob. According to the value, a specific subbitmap is displayed. Use a
CMultiFrameBitmap for its background bitmap.
*/
//------------------------------------------------------------------------
/**
* CAnimKnob constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background the background bitmap
*/
//------------------------------------------------------------------------
CAnimKnob::CAnimKnob (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background)
: CKnobBase (size, listener, tag, background), bInverseBitmap (false)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
heightOfOneImage = size.getHeight ();
setNumSubPixmaps (0);
if (background)
{
if (auto frameBitmap = dynamic_cast<CMultiFrameBitmap*> (background))
{
heightOfOneImage = frameBitmap->getFrameSize ().y;
setNumSubPixmaps (frameBitmap->getNumFrames ());
}
else
{
setNumSubPixmaps ((int32_t)(background->getHeight () / heightOfOneImage));
}
}
#endif
inset = 0;
}
//------------------------------------------------------------------------
CAnimKnob::CAnimKnob (const CAnimKnob& v)
: CKnobBase (v)
, bInverseBitmap (v.bInverseBitmap)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
setNumSubPixmaps (v.subPixmaps);
setHeightOfOneImage (v.heightOfOneImage);
#endif
}
//-----------------------------------------------------------------------------------------------
bool CAnimKnob::sizeToFit ()
{
if (auto bitmap = getDrawBackground ())
{
CRect vs (getViewSize ());
if (auto frameBitmap = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
vs.setSize (frameBitmap->getFrameSize ());
}
else
{
vs.setWidth (bitmap->getWidth ());
#if VSTGUI_ENABLE_DEPRECATED_METHODS
vs.setHeight (getHeightOfOneImage ());
#else
vs.setHeight (bitmap->getHeight ());
#endif
}
setViewSize (vs);
setMouseableArea (vs);
return true;
}
return false;
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
/**
* CAnimKnob constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param subPixmaps number of sub bitmaps in background
* @param heightOfOneImage the height of one sub bitmap
* @param background the background bitmap
* @param offset unused
*/
//------------------------------------------------------------------------
CAnimKnob::CAnimKnob (const CRect& size, IControlListener* listener, int32_t tag,
int32_t subPixmaps, CCoord heightOfOneImage, CBitmap* background,
const CPoint& offset)
: CKnobBase (size, listener, tag, background), bInverseBitmap (false)
{
vstgui_assert (background && !dynamic_cast<CMultiFrameBitmap*> (background),
"Use the other constrcutor when using a CMultiFrameBitmap");
setNumSubPixmaps (subPixmaps);
setHeightOfOneImage (heightOfOneImage);
inset = 0;
}
//-----------------------------------------------------------------------------------------------
void CAnimKnob::setHeightOfOneImage (const CCoord& height)
{
if (dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
return;
IMultiBitmapControl::setHeightOfOneImage (height);
if (getDrawBackground () && heightOfOneImage > 0)
setNumSubPixmaps ((int32_t)(getDrawBackground ()->getHeight () / heightOfOneImage));
}
#endif
//-----------------------------------------------------------------------------------------------
void CAnimKnob::setBackground (CBitmap *background)
{
CKnobBase::setBackground (background);
#if VSTGUI_ENABLE_DEPRECATED_METHODS
if (auto frameBitmap = dynamic_cast<CMultiFrameBitmap*> (background))
{
heightOfOneImage = frameBitmap->getFrameSize ().y;
setNumSubPixmaps (frameBitmap->getNumFrames ());
return;
}
if (heightOfOneImage == 0)
heightOfOneImage = getViewSize ().getHeight ();
if (background && heightOfOneImage > 0)
setNumSubPixmaps ((int32_t)(background->getHeight () / heightOfOneImage));
#endif
}
//------------------------------------------------------------------------
void CAnimKnob::draw (CDrawContext *pContext)
{
if (auto bitmap = getDrawBackground ())
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
auto frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
if (bInverseBitmap)
frameIndex = getInverseIndex (*mfb, frameIndex);
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
}
else
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint where (0, 0);
float val = getValueNormalized ();
if (val >= 0.f && heightOfOneImage > 0.)
{
CCoord tmp = heightOfOneImage * (getNumSubPixmaps () - 1);
if (bInverseBitmap)
where.y = floor ((1. - val) * tmp);
else
where.y = floor (val * tmp);
where.y -= (int32_t)where.y % (int32_t)heightOfOneImage;
}
bitmap->draw (pContext, getViewSize (), where);
#else
CView::draw (pContext);
#endif
}
}
setDirty (false);
}
} // VSTGUI
@@ -0,0 +1,202 @@
// 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
#pragma once
#include "ccontrol.h"
#include "../cbitmap.h"
#include "../ccolor.h"
#include "../clinestyle.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
class CKnobBase : public CControl, protected CMouseWheelEditingSupport
{
public:
//-----------------------------------------------------------------------------
/// @name CKnobBase Methods
//-----------------------------------------------------------------------------
//@{
virtual void valueToPoint (CPoint& point) const;
virtual float valueFromPoint (CPoint& point) const;
virtual void setStartAngle (float val);
virtual float getStartAngle () const { return startAngle; }
virtual void setRangeAngle (float val);
virtual float getRangeAngle () const { return rangeAngle; }
virtual void setZoomFactor (float val) { zoomFactor = val; }
virtual float getZoomFactor () const { return zoomFactor; }
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
void onMouseWheelEvent (MouseWheelEvent& event) override;
void onKeyboardEvent (KeyboardEvent& event) override;
void setViewSize (const CRect &rect, bool invalid = true) override;
bool sizeToFit () override;
void setMin (float val) override;
void setMax (float val) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
CLASS_METHODS_VIRTUAL(CKnobBase, CControl)
protected:
CKnobBase (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
CKnobBase (const CKnobBase& knob);
void compute ();
float startAngle, rangeAngle;
float zoomFactor;
float knobRange;
CCoord inset;
private:
struct MouseEditingState;
MouseEditingState& getMouseEditingState ();
void clearMouseEditingState ();
};
//-----------------------------------------------------------------------------
// CKnob Declaration
//! @brief a knob control
/// @ingroup controls
//-----------------------------------------------------------------------------
class CKnob : public CKnobBase
{
public:
enum DrawStyle {
kLegacyHandleLineDrawing = 0,
kHandleCircleDrawing = 1 << 0,
kCoronaDrawing = 1 << 1,
kCoronaFromCenter = 1 << 2,
kCoronaInverted = 1 << 3,
kCoronaLineDashDot = 1 << 4,
kCoronaOutline = 1 << 5,
kCoronaLineCapButt = 1 << 6,
kSkipHandleDrawing = 1 << 7,
};
CKnob (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, CBitmap* handle, const CPoint& offset = CPoint (0, 0), int32_t drawStyle = kLegacyHandleLineDrawing);
CKnob (const CKnob& knob);
//-----------------------------------------------------------------------------
/// @name CKnob Methods
//-----------------------------------------------------------------------------
//@{
int32_t getDrawStyle () const { return drawStyle; }
virtual void setDrawStyle (int32_t style);
CColor getCoronaColor () const { return coronaColor; }
virtual void setCoronaColor (CColor color);
CCoord getCoronaInset () const { return coronaInset; }
virtual void setCoronaInset (CCoord inset);
CColor getColorShadowHandle () const { return colorShadowHandle; }
virtual void setColorShadowHandle (CColor color);
CColor getColorHandle () const { return colorHandle; }
virtual void setColorHandle (CColor color);
CCoord getHandleLineWidth () const { return handleLineWidth; }
virtual void setHandleLineWidth (CCoord width);
CCoord getCoronaOutlineWidthAdd () const { return coronaOutlineWidthAdd; }
virtual void setCoronaOutlineWidthAdd (CCoord width);
const CLineStyle::CoordVector& getCoronaDashDotLengths () const;
virtual void setCoronaDashDotLengths (const CLineStyle::CoordVector& lengths);
CBitmap* getHandleBitmap () const { return pHandle; }
void setHandleBitmap (CBitmap* bitmap);
//@}
// overrides
void draw (CDrawContext* pContext) override;
bool getFocusPath (CGraphicsPath& outPath) override;
bool drawFocusOnTop () override;
CLASS_METHODS(CKnob, CKnobBase)
protected:
~CKnob () noexcept override;
virtual void drawHandle (CDrawContext* pContext);
virtual void drawCoronaOutline (CDrawContext* pContext) const;
virtual void drawCorona (CDrawContext* pContext) const;
virtual void drawHandleAsCircle (CDrawContext* pContext) const;
virtual void drawHandleAsLine (CDrawContext* pContext) const;
static void addArc (CGraphicsPath* path, const CRect& r, double startAngle, double sweepAngle);
CPoint offset;
int32_t drawStyle;
CColor colorHandle, colorShadowHandle, coronaColor;
CCoord handleLineWidth;
CCoord coronaInset;
CCoord coronaOutlineWidthAdd;
CLineStyle coronaLineStyle;
CBitmap* pHandle;
};
//-----------------------------------------------------------------------------
// CAnimKnob Declaration
//! @brief a bitmap knob control
/// @ingroup controls uses_multi_frame_bitmaps
//-----------------------------------------------------------------------------
class CAnimKnob : public CKnobBase,
public MultiFrameBitmapView<CAnimKnob>
#if VSTGUI_ENABLE_DEPRECATED_METHODS
,
public IMultiBitmapControl
#endif
{
public:
CAnimKnob (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
CAnimKnob (const CAnimKnob& knob);
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CAnimKnob (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
CCoord heightOfOneImage, CBitmap* background, const CPoint& offset = CPoint (0, 0));
void setHeightOfOneImage (const CCoord& height) override;
void setNumSubPixmaps (int32_t numSubPixmaps) override
{
IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps);
invalid ();
}
#endif
//-----------------------------------------------------------------------------
/// @name CAnimKnob Methods
//-----------------------------------------------------------------------------
//@{
void setInverseBitmap (bool val) { bInverseBitmap = val; }
bool getInverseBitmap () const { return bInverseBitmap; }
//@}
// overrides
void draw (CDrawContext* pContext) override;
bool sizeToFit () override;
void setBackground (CBitmap* background) override;
CLASS_METHODS(CAnimKnob, CKnobBase)
protected:
~CAnimKnob () noexcept override = default;
bool bInverseBitmap;
};
} // VSTGUI
@@ -0,0 +1,568 @@
// 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 "../cbitmap.h"
#include "../cdrawcontext.h"
#include "../cframe.h"
#include "../cgraphicspath.h"
#include "../cscrollview.h"
#include "../events.h"
#include "clistcontrol.h"
#include <vector>
//------------------------------------------------------------------------
namespace VSTGUI {
//------------------------------------------------------------------------
struct CListControl::Impl
{
SharedPointer<IListControlDrawer> drawer;
SharedPointer<IListControlConfigurator> configurator;
std::vector<CListControlRowDesc> rowDescriptions;
Optional<int32_t> hoveredRow {};
bool doHoverCheck {false};
CCoord minHeight {0.};
};
//------------------------------------------------------------------------
CListControl::CListControl (const CRect& size, IControlListener* listener, int32_t tag)
: CControl (size, listener, tag)
{
impl = std::unique_ptr<Impl> (new Impl);
}
//------------------------------------------------------------------------
CListControl::~CListControl () = default;
//------------------------------------------------------------------------
void CListControl::setDrawer (IListControlDrawer* d)
{
impl->drawer = d;
}
//------------------------------------------------------------------------
void CListControl::setConfigurator (IListControlConfigurator* c)
{
impl->configurator = c;
recalculateLayout ();
}
//------------------------------------------------------------------------
IListControlDrawer* CListControl::getDrawer () const
{
return impl->drawer;
}
//------------------------------------------------------------------------
IListControlConfigurator* CListControl::getConfigurator () const
{
return impl->configurator;
}
//------------------------------------------------------------------------
Optional<int32_t> CListControl::getHoveredRow () const
{
if (impl->hoveredRow)
return makeOptional (*impl->hoveredRow);
return {};
}
//------------------------------------------------------------------------
int32_t CListControl::getNumRows () const
{
auto numRows = static_cast<int32_t> (std::round (getRange ())) + 1;
return std::max (0, numRows);
}
//------------------------------------------------------------------------
int32_t CListControl::getIntValue () const
{
return static_cast<int32_t> (std::round (getValue ()));
}
//------------------------------------------------------------------------
void CListControl::recalculateLayout ()
{
if (!impl->configurator)
return;
CCoord height = 0.;
auto numRows = getNumRows ();
impl->rowDescriptions.resize (static_cast<size_t> (numRows));
impl->doHoverCheck = false;
for (auto row = 0; row < numRows; ++row)
{
impl->rowDescriptions[row] = impl->configurator->getRowDesc (row);
height += impl->rowDescriptions[row].height;
impl->doHoverCheck |= (impl->rowDescriptions[row].flags & CListControlRowDesc::Hoverable) != 0;
}
if (impl->minHeight > 0 && height < impl->minHeight)
height = impl->minHeight;
auto viewSize = getViewSize ();
if (viewSize.getHeight () != height)
{
viewSize.setHeight (height);
setViewSize (viewSize);
setMouseableArea (viewSize);
}
}
//------------------------------------------------------------------------
Optional<CRect> CListControl::getRowRect (int32_t row) const
{
if (row < getMinRowIndex () || row > getMaxRowIndex ())
return {};
row -= getMinRowIndex ();
CRect rowSize;
rowSize.setWidth (getWidth ());
for (auto i = 0u; i < impl->rowDescriptions.size (); ++i)
{
rowSize.setHeight (impl->rowDescriptions[i].height);
if (i == row)
break;
rowSize.offset (0, impl->rowDescriptions[i].height);
}
rowSize.offset (getViewSize ().getTopLeft ());
return makeOptional (rowSize);
}
//------------------------------------------------------------------------
void CListControl::invalidRow (int32_t row)
{
if (auto rect = getRowRect (row))
invalidRect (*rect);
}
//------------------------------------------------------------------------
Optional<int32_t> CListControl::getRowAtPoint (CPoint where) const
{
where.offsetInverse (getViewSize ().getTopLeft ());
auto numRows = getNumRows ();
for (auto row = 0; row < numRows; ++row)
{
if (where.y < impl->rowDescriptions[row].height)
return {row + getMinRowIndex ()};
where.y -= impl->rowDescriptions[row].height;
}
return {};
}
//------------------------------------------------------------------------
void CListControl::draw (CDrawContext* context)
{
drawRect (context, getViewSize ());
}
//------------------------------------------------------------------------
void CListControl::drawRect (CDrawContext* context, const CRect& updateRect)
{
setDirty (false);
ConcatClip cc (*context, updateRect);
if (cc.isEmpty ())
return;
if (auto bitmap = getDrawBackground ())
bitmap->draw (context, getViewSize ());
if (!impl->drawer)
return;
if (!getTransparency ())
impl->drawer->drawBackground (context, getViewSize ());
CRect rowSize;
rowSize.setTopLeft (getViewSize ().getTopLeft ());
rowSize.setWidth (getWidth ());
rowSize.setHeight (0);
auto numRows = getNumRows ();
auto selectedRow = static_cast<int32_t> (getNormalizedRowIndex (getIntValue ()));
for (auto row = 0; row < numRows; ++row)
{
rowSize.setHeight (impl->rowDescriptions[row].height);
if (updateRect.rectOverlap (rowSize))
{
IListControlDrawer::Row::Flags flags;
if (selectedRow == row)
flags = IListControlDrawer::Row::Selected;
if (impl->rowDescriptions[row].flags & CListControlRowDesc::Selectable)
flags |= IListControlDrawer::Row::Selectable;
if (impl->hoveredRow && *impl->hoveredRow == row + getMinRowIndex ())
flags |= IListControlDrawer::Row::Hovered;
if (row == numRows - 1)
flags |= IListControlDrawer::Row::LastRow;
impl->drawer->drawRow (context, rowSize, {row + getMinRowIndex (), flags});
}
rowSize.offset (0, impl->rowDescriptions[row].height);
}
}
//------------------------------------------------------------------------
bool CListControl::attached (CView* parent)
{
if (auto scrollView = dynamic_cast<CScrollView*> (parent->getParentView ()))
{
impl->minHeight = scrollView->calculateOptimalContainerSize ().getHeight ();
struct SizeListener : ViewListenerAdapter
{
SizeListener (CListControl* listControl, CScrollView* scrollView)
: control (listControl), scrollView (scrollView)
{
listControl->registerViewListener (this);
scrollView->registerViewListener (this);
}
~SizeListener () noexcept
{
control->unregisterViewListener (this);
scrollView->unregisterViewListener (this);
}
void viewSizeChanged (CView* view, const CRect& oldSize) override
{
if (view != scrollView)
return;
control->impl->minHeight =
scrollView->calculateOptimalContainerSize ().getHeight ();
control->recalculateLayout ();
}
void viewWillDelete (CView* view) override
{
if (view == control || view == scrollView)
delete this;
}
void viewAttached (CView* view) override {}
void viewRemoved (CView* view) override {}
CListControl* control {nullptr};
CScrollView* scrollView {nullptr};
};
new SizeListener (this, scrollView);
}
recalculateLayout ();
return CControl::attached (parent);
}
//------------------------------------------------------------------------
void CListControl::setMin (float val)
{
if (getMin () != val && val < getMax ())
{
auto ov = getValue ();
CControl::setMin (val);
if (isAttached ())
recalculateLayout ();
if (ov != getValue ())
valueChanged ();
}
}
//------------------------------------------------------------------------
void CListControl::setMax (float val)
{
if (getMax () != val && val >= getMin ())
{
auto ov = getValue ();
CControl::setMax (val);
if (isAttached ())
recalculateLayout ();
if (ov != getValue ())
valueChanged ();
}
}
//------------------------------------------------------------------------
size_t CListControl::getNormalizedRowIndex (int32_t row) const
{
vstgui_assert (row >= getMinRowIndex ());
return row - getMinRowIndex ();
}
//------------------------------------------------------------------------
CMouseEventResult CListControl::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (buttons.isLeftButton ())
return kMouseEventHandled;
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
//------------------------------------------------------------------------
void CListControl::clearHoveredRow ()
{
if (impl->hoveredRow)
{
invalidRow (*impl->hoveredRow);
impl->hoveredRow.reset ();
}
}
//------------------------------------------------------------------------
CMouseEventResult CListControl::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
if (impl->doHoverCheck)
{
auto row = getRowAtPoint (where);
if (row)
{
if (impl->rowDescriptions[getNormalizedRowIndex (*row)].flags &
CListControlRowDesc::Hoverable)
{
if (!impl->hoveredRow || *impl->hoveredRow != *row)
{
clearHoveredRow ();
impl->hoveredRow = makeOptional (*row);
invalidRow (*row);
}
}
else
clearHoveredRow ();
}
else
clearHoveredRow ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CListControl::onMouseUp (CPoint& where, const CButtonState& buttons)
{
if (impl->rowDescriptions.empty () || !buttons.isLeftButton ())
return kMouseEventHandled;
auto row = getRowAtPoint (where);
if (row && getIntValue () != *row)
{
if (rowSelectable (*row))
{
invalidRow (getIntValue ());
beginEdit ();
setValue (static_cast<float> (*row));
valueChanged ();
endEdit ();
invalidRow (getIntValue ());
}
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CListControl::onMouseExited (CPoint& where, const CButtonState& buttons)
{
clearHoveredRow ();
return kMouseEventHandled;
}
//------------------------------------------------------------------------
int32_t CListControl::getMinRowIndex () const
{
return static_cast<int32_t> (getMin ());
}
//------------------------------------------------------------------------
int32_t CListControl::getMaxRowIndex () const
{
return static_cast<int32_t> (getMax ());
}
//------------------------------------------------------------------------
int32_t CListControl::getNextSelectableRow (int32_t r, int32_t direction) const
{
auto minRowIndex = getMinRowIndex ();
auto maxRowIndex = getMaxRowIndex ();
int32_t row = r;
do
{
row += direction;
if (row > maxRowIndex)
row = minRowIndex;
else if (row < minRowIndex)
row = maxRowIndex;
if (rowSelectable (row))
break;
} while (row != r);
return row;
}
//------------------------------------------------------------------------
bool CListControl::rowSelectable (int32_t row) const
{
return (impl->rowDescriptions[getNormalizedRowIndex (row)].flags &
CListControlRowDesc::Selectable) != 0;
}
//------------------------------------------------------------------------
void CListControl::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type != EventType::KeyDown)
return;
if (getMouseEnabled () && event.character == 0)
{
int32_t newRow = getIntValue ();
switch (event.virt)
{
default: return;
case VirtualKey::Home:
{
if (!event.modifiers.empty ())
break;
newRow = getMinRowIndex ();
if (!rowSelectable (newRow))
newRow = getNextSelectableRow (newRow, 1);
break;
}
case VirtualKey::End:
{
if (!event.modifiers.empty ())
break;
newRow = getMaxRowIndex ();
if (!rowSelectable (newRow))
newRow = getNextSelectableRow (newRow, -1);
break;
}
case VirtualKey::Up:
{
if (!event.modifiers.empty ())
break;
newRow = getNextSelectableRow (newRow, -1);
break;
}
case VirtualKey::Down:
{
if (!event.modifiers.empty ())
break;
newRow = getNextSelectableRow (newRow, 1);
break;
}
case VirtualKey::PageUp:
{
if (!event.modifiers.empty ())
break;
auto vr = getVisibleViewSize ();
auto rr = getRowRect (newRow);
if (rr && !vr.rectOverlap (*rr))
{
if (auto parent = getParentView ())
{
if (auto scrollView = dynamic_cast<CScrollView*> (parent->getParentView ()))
{
scrollView->makeRectVisible (*rr);
onKeyboardEvent (event);
return;
}
}
}
vr.top += 2;
if (auto firstVisibleRow = getRowAtPoint (vr.getTopLeft ()))
{
while (!rowSelectable (*firstVisibleRow))
*firstVisibleRow += 1;
if (*firstVisibleRow == getIntValue ())
{
vr.offset (0, -vr.getHeight ());
if ((firstVisibleRow = getRowAtPoint (vr.getTopLeft ())))
newRow = *firstVisibleRow;
else
newRow = getMinRowIndex ();
}
else
{
newRow = *firstVisibleRow;
}
}
if (!rowSelectable (newRow))
newRow = getNextSelectableRow (newRow, -1);
break;
}
case VirtualKey::PageDown:
{
if (!event.modifiers.empty ())
break;
auto vr = getVisibleViewSize ();
auto rr = getRowRect (newRow);
if (rr && !vr.rectOverlap (*rr))
{
if (auto parent = getParentView ())
{
if (auto scrollView = dynamic_cast<CScrollView*> (parent->getParentView ()))
{
scrollView->makeRectVisible (*rr);
onKeyboardEvent (event);
return;
}
}
}
vr.bottom -= 2;
if (auto lastVisibleRow = getRowAtPoint (vr.getBottomLeft ()))
{
while (!rowSelectable (*lastVisibleRow))
*lastVisibleRow -= 1;
if (*lastVisibleRow == getIntValue ())
{
vr.offset (0, vr.getHeight ());
if ((lastVisibleRow = getRowAtPoint (vr.getBottomLeft ())))
newRow = *lastVisibleRow;
else
newRow = getMaxRowIndex ();
}
else
{
newRow = *lastVisibleRow;
}
}
if (!rowSelectable (newRow))
newRow = getNextSelectableRow (newRow, 1);
break;
}
}
if (newRow != getIntValue () && rowSelectable (newRow))
{
invalidRow (getIntValue ());
beginEdit ();
setValue (static_cast<float> (newRow));
valueChanged ();
endEdit ();
if (auto rowRect = getRowRect (getIntValue ()))
{
invalidRect (*rowRect);
if (auto parent = getParentView ())
{
if (auto scrollView = dynamic_cast<CScrollView*> (parent->getParentView ()))
scrollView->makeRectVisible (*rowRect);
}
}
event.consumed = true;
}
}
}
//------------------------------------------------------------------------
void CListControl::setViewSize (const CRect& rect, bool invalid)
{
CControl::setViewSize (rect, invalid);
impl->hoveredRow.reset ();
}
//------------------------------------------------------------------------
bool CListControl::drawFocusOnTop ()
{
return true;
}
//------------------------------------------------------------------------
bool CListControl::getFocusPath (CGraphicsPath& outPath)
{
CRect r = getVisibleViewSize ();
outPath.addRect (r);
CCoord focusWidth = getFrame ()->getFocusWidth ();
r.inset (focusWidth, focusWidth);
outPath.addRect (r);
return true;
}
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,197 @@
// 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
#pragma once
#include "../optional.h"
#include "../enumbitset.h"
#include "ccontrol.h"
//------------------------------------------------------------------------
namespace VSTGUI {
//------------------------------------------------------------------------
/** Control which draws a list of configurable rows
*
* This control needs to be setup with an instance of a IListControlDrawer and a
* IListControlConfigurator. The number of rows is configured via the min and max values. And the
* selected row is the value of this control.
* The actual drawing is done via the IListControlDrawer instance. And the row configuration is
* handled via the IListControlConfigurator instance. Every row can have different heights and
* flags.
*
* @ingroup new_in_4_9
*/
//------------------------------------------------------------------------
class CListControl final : public CControl
{
public:
CListControl (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1);
~CListControl () override;
void setDrawer (IListControlDrawer* d);
void setConfigurator (IListControlConfigurator* c);
IListControlDrawer* getDrawer () const;
IListControlConfigurator* getConfigurator () const;
void recalculateLayout ();
void invalidRow (int32_t row);
Optional<int32_t> getRowAtPoint (CPoint where) const;
Optional<CRect> getRowRect (int32_t row) const;
Optional<int32_t> getHoveredRow () const;
int32_t getIntValue () const;
int32_t getNumRows () const;
// overrides
void setMin (float val) override;
void setMax (float val) override;
bool attached (CView* parent) override;
void draw (CDrawContext* context) override;
void drawRect (CDrawContext* context, const CRect& updateRect) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) override;
void onKeyboardEvent (KeyboardEvent& event) override;
void setViewSize (const CRect& rect, bool invalid = true) override;
bool drawFocusOnTop () override;
bool getFocusPath (CGraphicsPath& outPath) override;
CLASS_METHODS_NOCOPY (CListControl, CControl)
private:
int32_t getNextSelectableRow (int32_t r, int32_t direction) const;
int32_t getMinRowIndex () const;
int32_t getMaxRowIndex () const;
size_t getNormalizedRowIndex (int32_t row) const;
bool rowSelectable (int32_t row) const;
void clearHoveredRow ();
struct Impl;
std::unique_ptr<Impl> impl;
};
//------------------------------------------------------------------------
/** The description of one row for the CListControl
*
* This is returned by an instance of IListControlConfigurator for every row.
*
* @ingroup new_in_4_9
*/
//------------------------------------------------------------------------
struct CListControlRowDesc
{
enum Flag : uint32_t
{
/** Indicates that the row is selectable */
Selectable = 1 << 0,
/** Indicates that the row should be redrawn when the mouse hovers it */
Hoverable = 1 << 1,
};
using Flags = EnumBitset<Flag, true>;
/** The height of the row */
CCoord height {0};
/** The flags of the row, see the Flags enum above */
Flags flags {Selectable};
CListControlRowDesc () = default;
CListControlRowDesc (CCoord h, Flags f) : height (h), flags (f) {}
};
//------------------------------------------------------------------------
/** The list control drawer interface
*
* This is used to do the actual drawing of the list control.
*
* @ingroup new_in_4_9
*/
//------------------------------------------------------------------------
class IListControlDrawer : virtual public IReference
{
public:
virtual ~IListControlDrawer () noexcept {}
struct Row
{
enum Flag : uint32_t
{
Selectable = 1 << 0,
Selected = 1 << 1,
Hovered = 1 << 2,
LastRow = 1 << 3,
};
using Flags = EnumBitset<Flag, true>;
operator int32_t () const { return getIndex (); }
int32_t getIndex () const { return index; }
bool isSelectable () const { return (flags & Selectable) != 0; }
bool isSelected () const { return (flags & Selected) != 0; }
bool isHovered () const { return (flags & Hovered) != 0; }
bool isLastRow () const { return (flags & LastRow) != 0; }
Row (int32_t index, Flags flags) : index (index), flags (flags) {}
private:
int32_t index;
Flags flags;
};
virtual void drawBackground (CDrawContext* context, CRect size) = 0;
virtual void drawRow (CDrawContext* context, CRect size, Row row) = 0;
};
//------------------------------------------------------------------------
/** The list control configurator interface
*
* @ingroup new_in_4_9
*/
//------------------------------------------------------------------------
class IListControlConfigurator : virtual public IReference
{
public:
virtual ~IListControlConfigurator () noexcept {}
virtual CListControlRowDesc getRowDesc (int32_t row) const = 0;
};
//------------------------------------------------------------------------
/** A list control configurator implementation.
*
* Returns the same row description for all row indices
*
* @ingroup new_in_4_9
*/
//------------------------------------------------------------------------
class StaticListControlConfigurator : public IListControlConfigurator,
public NonAtomicReferenceCounted
{
public:
using Flags = CListControlRowDesc::Flags;
StaticListControlConfigurator (CCoord inRowHeight,
Flags inFlags = {CListControlRowDesc::Selectable,
CListControlRowDesc::Hoverable})
: rowHeight (inRowHeight), flags (inFlags)
{
}
void setRowHeight (CCoord height) { rowHeight = height; }
void setFlags (Flags f) { flags = f; }
CCoord getRowHeight () const { return rowHeight; }
Flags getFlags () const { return flags; }
CListControlRowDesc getRowDesc (int32_t row) const override { return {rowHeight, flags}; }
private:
CCoord rowHeight;
Flags flags;
};
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,133 @@
// 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 "cmoviebitmap.h"
#include "../cdrawcontext.h"
#include "../cbitmap.h"
namespace VSTGUI {
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
bool CMovieBitmap::useLegacyFrameCalculation = false;
#endif
//------------------------------------------------------------------------
// CMovieBitmap
//------------------------------------------------------------------------
/**
* CMovieBitmap constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background bitmap
*/
//------------------------------------------------------------------------
CMovieBitmap::CMovieBitmap (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background)
: CControl (size, listener, tag, background)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
setHeightOfOneImage (size.getHeight ());
setNumSubPixmaps (background ? (int32_t)(background->getHeight () / heightOfOneImage) : 0);
#endif
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
/**
* CMovieBitmap constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param subPixmaps number of subPixmaps
* @param heightOfOneImage height of one image in pixel
* @param background bitmap
* @param offset
*/
//------------------------------------------------------------------------
CMovieBitmap::CMovieBitmap (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps, CCoord heightOfOneImage, CBitmap* background, const CPoint &offset)
: CControl (size, listener, tag, background)
, offset (offset)
{
setNumSubPixmaps (subPixmaps);
setHeightOfOneImage (heightOfOneImage);
}
#endif
//------------------------------------------------------------------------
CMovieBitmap::CMovieBitmap (const CMovieBitmap& v) : CControl (v)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
offset = v.offset;
setNumSubPixmaps (v.subPixmaps);
setHeightOfOneImage (v.heightOfOneImage);
#endif
}
//------------------------------------------------------------------------
void CMovieBitmap::draw (CDrawContext *pContext)
{
if (auto bitmap = getDrawBackground ())
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
auto frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
}
else
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
#include "../private/disabledeprecatedmessage.h"
CPoint where (offset.x, offset.y);
if (useLegacyFrameCalculation)
{
where.y += heightOfOneImage *
(int32_t)(getValueNormalized () * (getNumSubPixmaps () - 1) + 0.5);
}
else
{
auto step = static_cast<int32_t> (std::min (
getNumSubPixmaps () - 1.f, getValueNormalized () * getNumSubPixmaps ()));
where.y += heightOfOneImage * step;
}
bitmap->draw (pContext, getViewSize (), where);
#include "../private/enabledeprecatedmessage.h"
#else
bitmap->draw (pContext, getViewSize ());
#endif
}
}
setDirty (false);
}
//-----------------------------------------------------------------------------------------------
bool CMovieBitmap::sizeToFit ()
{
if (auto bitmap = getDrawBackground ())
{
CRect vs (getViewSize ());
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
vs.setSize (mfb->getFrameSize ());
}
else
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
vs.setHeight (getHeightOfOneImage ());
#else
vs.setHeight (bitmap->getHeight ());
#endif
vs.setWidth (bitmap->getWidth ());
}
setViewSize (vs);
setMouseableArea (vs);
return true;
}
return false;
}
} // VSTGUI
@@ -0,0 +1,52 @@
// 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
#pragma once
#include "ccontrol.h"
#include "../cbitmap.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CMovieBitmap Declaration
//! @brief a bitmap view that displays different bitmaps according to its current value
///
/// Use a CMultiFrameBitmap for its background bitmap.
///
/// @ingroup views uses_multi_frame_bitmaps
//-----------------------------------------------------------------------------
class CMovieBitmap : public CControl,
public MultiFrameBitmapView<CMovieBitmap>
#if VSTGUI_ENABLE_DEPRECATED_METHODS
,
public IMultiBitmapControl
#endif
{
public:
CMovieBitmap (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
CMovieBitmap (const CMovieBitmap& movieBitmap);
void draw (CDrawContext*) override;
bool sizeToFit () override;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CMovieBitmap (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
CCoord heightOfOneImage, CBitmap* background,
const CPoint& offset = CPoint (0, 0));
void setNumSubPixmaps (int32_t numSubPixmaps) override { IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps); invalid (); }
#endif
VSTGUI_DEPRECATED_MSG (static bool useLegacyFrameCalculation;
, "Use CMultiFrameBitmap::normalizedValueToFrameIndex() instead")
CLASS_METHODS(CMovieBitmap, CControl)
protected:
~CMovieBitmap () noexcept override = default;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint offset {};
#endif
};
} // VSTGUI
@@ -0,0 +1,189 @@
// 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 "cmoviebutton.h"
#include "../cdrawcontext.h"
#include "../cbitmap.h"
#include "../events.h"
namespace VSTGUI {
//------------------------------------------------------------------------
// CMovieButton
//------------------------------------------------------------------------
/**
* CMovieButton constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background bitmap
*/
//------------------------------------------------------------------------
CMovieButton::CMovieButton (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background)
: CControl (size, listener, tag, background), buttonState (value)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
heightOfOneImage = size.getHeight ();
#endif
setWantsFocus (true);
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
/**
* CMovieButton constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param heightOfOneImage height of one image in pixel
* @param background bitmap
* @param offset
*/
//------------------------------------------------------------------------
CMovieButton::CMovieButton (const CRect& size, IControlListener* listener, int32_t tag, CCoord heightOfOneImage, CBitmap* background, const CPoint &offset)
: CControl (size, listener, tag, background)
, offset (offset)
, buttonState (value)
{
setHeightOfOneImage (heightOfOneImage);
setWantsFocus (true);
}
#endif
//------------------------------------------------------------------------
CMovieButton::CMovieButton (const CMovieButton& v) : CControl (v), buttonState (v.buttonState)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
offset = v.offset;
setHeightOfOneImage (v.heightOfOneImage);
#endif
setWantsFocus (true);
}
//------------------------------------------------------------------------
void CMovieButton::draw (CDrawContext *pContext)
{
if (auto bitmap = getDrawBackground ())
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
auto frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
}
else
{
CPoint where {};
#if VSTGUI_ENABLE_DEPRECATED_METHODS
if (value == getMax ())
where.y = heightOfOneImage;
#endif
bitmap->draw (pContext, getViewSize (), where);
}
}
buttonState = value;
setDirty (false);
}
//------------------------------------------------------------------------
CMouseEventResult CMovieButton::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (!(buttons & kLButton))
return kMouseEventNotHandled;
fEntryState = value;
beginEdit ();
return onMouseMoved (where, buttons);
}
//------------------------------------------------------------------------
CMouseEventResult CMovieButton::onMouseUp (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
endEdit ();
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CMovieButton::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
if (where.x >= getViewSize ().left &&
where.y >= getViewSize ().top &&
where.x <= getViewSize ().right &&
where.y <= getViewSize ().bottom)
value = (fEntryState == getMax ()) ? getMin () : getMax ();
else
value = fEntryState;
if (isDirty ())
{
valueChanged ();
invalid ();
}
return kMouseEventHandled;
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CMovieButton::onMouseCancel ()
{
if (isEditing ())
{
value = fEntryState;
if (isDirty ())
{
valueChanged ();
invalid ();
}
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
void CMovieButton::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type != EventType::KeyDown || event.modifiers.empty () == false)
return;
if (event.virt == VirtualKey::Return)
{
value = (value == getMax ()) ? getMin () : getMax ();
invalid ();
beginEdit ();
valueChanged ();
endEdit ();
event.consumed = true;
}
}
//-----------------------------------------------------------------------------------------------
bool CMovieButton::sizeToFit ()
{
if (auto bitmap = getDrawBackground ())
{
CRect vs (getViewSize ());
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
vs.setSize (mfb->getFrameSize ());
}
else
{
vs.setWidth (bitmap->getWidth ());
#if VSTGUI_ENABLE_DEPRECATED_METHODS
vs.setHeight (getHeightOfOneImage ());
#else
vs.setHeight (bitmap->getHeight ());
#endif
}
setViewSize (vs);
setMouseableArea (vs);
return true;
}
return false;
}
} // VSTGUI
@@ -0,0 +1,59 @@
// 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
#pragma once
#include "ccontrol.h"
#include "../cbitmap.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CMovieButton Declaration
//! @brief a bi-states button with 2 subbitmaps
///
/// Use a CMultiFrameBitmap for its background bitmap.
///
/// @ingroup controls uses_multi_frame_bitmaps
//-----------------------------------------------------------------------------
class CMovieButton : public CControl,
public MultiFrameBitmapView<CMovieButton>
#if VSTGUI_ENABLE_DEPRECATED_METHODS
,
public IMultiBitmapControl
#endif
{
public:
CMovieButton (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
CMovieButton (const CMovieButton& movieButton);
void draw (CDrawContext*) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
void onKeyboardEvent (KeyboardEvent& event) override;
bool sizeToFit () override;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CMovieButton (const CRect& size, IControlListener* listener, int32_t tag,
CCoord heightOfOneImage, CBitmap* background,
const CPoint& offset = CPoint (0, 0));
void setNumSubPixmaps (int32_t numSubPixmaps) override { IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps); invalid (); }
#endif
CLASS_METHODS(CMovieButton, CControl)
protected:
~CMovieButton () noexcept override = default;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint offset {};
#endif
float buttonState;
private:
float fEntryState;
};
} // VSTGUI
@@ -0,0 +1,918 @@
// 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 "coptionmenu.h"
#include "../cbitmap.h"
#include "../cframe.h"
#include "../cstring.h"
#include "../events.h"
#include "../platform/iplatformoptionmenu.h"
#include "../platform/iplatformframe.h"
namespace VSTGUI {
//------------------------------------------------------------------------
struct CMenuItem::Impl
{
UTF8String title;
UTF8String keyCode;
SharedPointer<COptionMenu> submenu;
SharedPointer<CBitmap> icon;
int32_t flags {0};
int32_t keyModifiers {0};
VirtualKey virtualKey {VirtualKey::None};
int32_t tag {-1};
};
//------------------------------------------------------------------------
// CMenuItem
//------------------------------------------------------------------------
/*! @class CMenuItem
Defines an item of a VSTGUI::COptionMenu
*/
//------------------------------------------------------------------------
CMenuItem::CMenuItem ()
{
impl = std::make_unique<Impl> ();
}
//------------------------------------------------------------------------
CMenuItem::~CMenuItem () noexcept = default;
//------------------------------------------------------------------------
/**
* CMenuItem constructor.
* @param inTitle title of item
* @param inFlags CMenuItem::Flags of item
* @param inKeycode keycode of item
* @param inKeyModifiers keymodifiers of item
* @param inIcon icon of item
*/
//------------------------------------------------------------------------
CMenuItem::CMenuItem (const UTF8String& inTitle, const UTF8String& inKeycode, int32_t inKeyModifiers, CBitmap* inIcon, int32_t inFlags)
: CMenuItem ()
{
impl->flags = inFlags;
setTitle (inTitle);
setKey (inKeycode, inKeyModifiers);
setIcon (inIcon);
}
//------------------------------------------------------------------------
/**
* CMenuItem constructor.
* @param inTitle title of item
* @param inSubmenu submenu of item
* @param inIcon icon of item
*/
//------------------------------------------------------------------------
CMenuItem::CMenuItem (const UTF8String& inTitle, COptionMenu* inSubmenu, CBitmap* inIcon)
: CMenuItem ()
{
setTitle (inTitle);
setSubmenu (inSubmenu);
setIcon (inIcon);
}
//------------------------------------------------------------------------
/**
* CMenuItem constructor.
* @param inTitle title of item
* @param inTag tag of item
*/
//------------------------------------------------------------------------
CMenuItem::CMenuItem (const UTF8String& inTitle, int32_t inTag)
: CMenuItem ()
{
setTitle (inTitle);
setTag (inTag);
}
//------------------------------------------------------------------------
/**
* CMenuItem copy constructor.
* @param item item to copy
*/
//------------------------------------------------------------------------
CMenuItem::CMenuItem (const CMenuItem& item)
: CMenuItem ()
{
impl->flags = item.impl->flags;
setTitle (item.getTitle ());
setIcon (item.getIcon ());
if (item.getVirtualKey () != VirtualKey::None)
setVirtualKey (item.getVirtualKey (), item.getKeyModifiers ());
else
setKey (item.getKeycode (), item.getKeyModifiers ());
setTag (item.getTag ());
setSubmenu (item.getSubmenu ());
}
//------------------------------------------------------------------------
void CMenuItem::setTitle (const UTF8String& inTitle)
{
impl->title = inTitle;
}
//------------------------------------------------------------------------
void CMenuItem::setKey (const UTF8String& inKeycode, int32_t inKeyModifiers)
{
impl->keyCode = inKeycode;
impl->keyModifiers = inKeyModifiers;
impl->virtualKey = VirtualKey::None;
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
void CMenuItem::setVirtualKey (int32_t inVirtualKeyCode, int32_t inKeyModifiers)
{
setKey (nullptr, inKeyModifiers);
impl->virtualKey = fromVstVirtualKey (inVirtualKeyCode);
}
//------------------------------------------------------------------------
int32_t CMenuItem::getVirtualKeyCode () const
{
return toVstVirtualKey (impl->virtualKey);
}
#endif
//------------------------------------------------------------------------
void CMenuItem::setVirtualKey (VirtualKey inVirtualKey, int32_t inKeyModifiers)
{
setKey (nullptr, inKeyModifiers);
impl->virtualKey = inVirtualKey;
}
//------------------------------------------------------------------------
void CMenuItem::setSubmenu (COptionMenu* inSubmenu)
{
impl->submenu = inSubmenu;
}
//------------------------------------------------------------------------
void CMenuItem::setIcon (CBitmap* inIcon)
{
impl->icon = inIcon;
}
//------------------------------------------------------------------------
void CMenuItem::setTag (int32_t t)
{
impl->tag = t;
}
//------------------------------------------------------------------------
void CMenuItem::setEnabled (bool state)
{
setBit (impl->flags, kDisabled, !state);
}
//------------------------------------------------------------------------
void CMenuItem::setChecked (bool state)
{
setBit (impl->flags, kChecked, state);
}
//------------------------------------------------------------------------
void CMenuItem::setIsTitle (bool state)
{
setBit (impl->flags, kTitle, state);
}
//------------------------------------------------------------------------
void CMenuItem::setIsSeparator (bool state)
{
setBit (impl->flags, kSeparator, state);
}
//------------------------------------------------------------------------
bool CMenuItem::isEnabled () const
{
return !hasBit (impl->flags, kDisabled);
}
//------------------------------------------------------------------------
bool CMenuItem::isChecked () const
{
return hasBit (impl->flags, kChecked);
}
//------------------------------------------------------------------------
bool CMenuItem::isTitle () const
{
return hasBit (impl->flags, kTitle);
}
//------------------------------------------------------------------------
bool CMenuItem::isSeparator () const
{
return hasBit (impl->flags, kSeparator);
}
//------------------------------------------------------------------------
const UTF8String& CMenuItem::getTitle () const
{
return impl->title;
}
//------------------------------------------------------------------------
int32_t CMenuItem::getKeyModifiers () const
{
return impl->keyModifiers;
}
//------------------------------------------------------------------------
const UTF8String& CMenuItem::getKeycode () const
{
return impl->keyCode;
}
//------------------------------------------------------------------------
VirtualKey CMenuItem::getVirtualKey () const
{
return impl->virtualKey;
}
//------------------------------------------------------------------------
COptionMenu* CMenuItem::getSubmenu () const
{
return impl->submenu;
}
//------------------------------------------------------------------------
CBitmap* CMenuItem::getIcon () const
{
return impl->icon;
}
//------------------------------------------------------------------------
int32_t CMenuItem::getTag () const
{
return impl->tag;
}
//------------------------------------------------------------------------
/*! @class CCommandMenuItem
The CCommandMenuItem supports setting a category, name and a target. The target will get a @link CBaseObject::notify notify()@endlink call before the item is
displayed and after it was selected. @see CCommandMenuItem::kMsgMenuItemValidate and @see CCommandMenuItem::kMsgMenuItemSelected
*/
//------------------------------------------------------------------------
CCommandMenuItem::CCommandMenuItem (Desc&& args)
: CMenuItem (args.title, args.keycode, args.keyModifiers, args.icon, args.flags)
, commandCategory (std::move (args.commandCategory))
, commandName (std::move (args.commandName))
, itemTarget (std::move (args.target))
{
setTag (args.tag);
}
//------------------------------------------------------------------------
CCommandMenuItem::CCommandMenuItem (const Desc& args)
: CMenuItem (args.title, args.keycode, args.keyModifiers, args.icon, args.flags)
, commandCategory (args.commandCategory)
, commandName (args.commandName)
, itemTarget (args.target)
{
setTag (args.tag);
}
//------------------------------------------------------------------------
CCommandMenuItem::CCommandMenuItem (const CCommandMenuItem& item)
: CMenuItem (item)
, validateFunc (item.validateFunc)
, selectedFunc (item.selectedFunc)
, commandCategory (item.commandCategory)
, commandName (item.commandName)
{
setItemTarget (item.itemTarget);
}
//------------------------------------------------------------------------
void CCommandMenuItem::setItemTarget (ICommandMenuItemTarget* target)
{
itemTarget = target;
}
//------------------------------------------------------------------------
void CCommandMenuItem::setCommandCategory (const UTF8String& category)
{
commandCategory = category;
}
//------------------------------------------------------------------------
bool CCommandMenuItem::isCommandCategory (const UTF8String& category) const
{
return commandCategory == category;
}
//------------------------------------------------------------------------
void CCommandMenuItem::setCommandName (const UTF8String& name)
{
commandName = name;
}
//------------------------------------------------------------------------
bool CCommandMenuItem::isCommandName (const UTF8String& name) const
{
return commandName == name;
}
//------------------------------------------------------------------------
void CCommandMenuItem::setActions (SelectedCallbackFunction&& selected, ValidateCallbackFunction&& validate)
{
selectedFunc = std::move (selected);
validateFunc = std::move (validate);
}
//------------------------------------------------------------------------
void CCommandMenuItem::execute ()
{
if (selectedFunc)
selectedFunc (this);
if (itemTarget)
itemTarget->onCommandMenuItemSelected (this);
}
//------------------------------------------------------------------------
void CCommandMenuItem::validate ()
{
if (validateFunc)
validateFunc (this);
if (itemTarget)
itemTarget->validateCommandMenuItem (this);
}
//------------------------------------------------------------------------
// COptionMenu
//------------------------------------------------------------------------
/*! @class COptionMenu
Define a rectangle view where a text-value can be displayed with a given font and color.
The text-value is centered in the given rect.
A bitmap can be used as background, a second bitmap can be used when the option menu is popuped.
There are 2 styles with or without a shadowed text. When a mouse click occurs, a popup menu is displayed.
*/
//------------------------------------------------------------------------
/**
* COptionMenu constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background the background bitmap
* @param bgWhenClick the background bitmap if the option menu is displayed
* @param style the style of the display (see CParamDisplay for styles)
*/
//------------------------------------------------------------------------
COptionMenu::COptionMenu (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, CBitmap* bgWhenClick, const int32_t style)
: CParamDisplay (size, background, style)
, bgWhenClick (bgWhenClick)
{
this->listener = listener;
this->tag = tag;
lastButton = kRButton;
menuItems = new CMenuItemList;
setWantsFocus (true);
}
//------------------------------------------------------------------------
COptionMenu::COptionMenu ()
: CParamDisplay (CRect (0, 0, 0, 0))
{
menuItems = new CMenuItemList;
setWantsFocus (true);
}
//------------------------------------------------------------------------
COptionMenu::COptionMenu (const COptionMenu& v)
: CParamDisplay (v)
, menuItems (new CMenuItemList (*v.menuItems))
, nbItemsPerColumn (v.nbItemsPerColumn)
, bgWhenClick (v.bgWhenClick)
{
setWantsFocus (true);
}
//------------------------------------------------------------------------
COptionMenu::~COptionMenu () noexcept
{
removeAllEntry ();
delete menuItems;
}
//------------------------------------------------------------------------
void COptionMenu::registerOptionMenuListener (IOptionMenuListener* listener)
{
if (!listeners)
listeners = std::unique_ptr<MenuListenerList> (new MenuListenerList ());
listeners->add (listener);
}
//------------------------------------------------------------------------
void COptionMenu::unregisterOptionMenuListener (IOptionMenuListener* listener)
{
if (listeners)
listeners->remove (listener);
}
//------------------------------------------------------------------------
void COptionMenu::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type != EventType::KeyUp && event.modifiers.empty () && event.character == 0)
{
if (event.virt == VirtualKey::Return)
{
auto self = shared (this);
getFrame ()->doAfterEventProcessing ([self] () {
self->doPopup ();
});
event.consumed = true;
return;
}
if (!(style & (kMultipleCheckStyle & ~kCheckStyle)))
{
if (event.virt == VirtualKey::Up)
{
int32_t value = (int32_t)getValue ()-1;
if (value >= 0)
{
CMenuItem* entry = getEntry (value);
while (entry && (entry->isSeparator () || entry->isTitle () || !entry->isEnabled () || entry->getSubmenu ()))
entry = getEntry (--value);
if (entry)
{
beginEdit ();
setValue ((float)value);
lastResult = (int32_t)getValue ();
valueChanged ();
endEdit ();
invalid ();
}
}
event.consumed = true;
return;
}
if (event.virt == VirtualKey::Down)
{
int32_t value = (int32_t)getValue ()+1;
if (value < getNbEntries ())
{
CMenuItem* entry = getEntry (value);
while (entry && (entry->isSeparator () || entry->isTitle () || !entry->isEnabled () || entry->getSubmenu ()))
entry = getEntry (++value);
if (entry)
{
beginEdit ();
setValue ((float)value);
lastResult = (int32_t)getValue ();
valueChanged ();
endEdit ();
invalid ();
}
}
event.consumed = true;
return;
}
}
}
CParamDisplay::onKeyboardEvent (event);
}
//------------------------------------------------------------------------
void COptionMenu::beforePopup ()
{
if (listeners)
listeners->forEach ([this] (IOptionMenuListener* l) { l->onOptionMenuPrePopup (this); });
for (auto& menuItem : *menuItems)
{
if (auto* commandItem = menuItem.cast<CCommandMenuItem> ())
commandItem->validate ();
if (menuItem->getSubmenu ())
menuItem->getSubmenu ()->beforePopup ();
}
}
//------------------------------------------------------------------------
void COptionMenu::afterPopup ()
{
for (auto& menuItem : *menuItems)
{
if (menuItem->getSubmenu ())
menuItem->getSubmenu ()->afterPopup ();
}
if (listeners)
listeners->forEach ([this] (IOptionMenuListener* l) { l->onOptionMenuPostPopup (this); });
}
//------------------------------------------------------------------------
bool COptionMenu::doPopup ()
{
if (bgWhenClick)
invalid ();
auto result = popup ();
if (bgWhenClick)
invalid ();
return result;
}
//------------------------------------------------------------------------
bool COptionMenu::popup (const PopupCallback& callback)
{
if (!getFrame ())
return false;
beforePopup ();
lastResult = -1;
lastMenu = nullptr;
if (!menuItems->empty ())
{
getFrame ()->onStartLocalEventLoop ();
if (auto platformMenu = getFrame ()->getPlatformFrame ()->createPlatformOptionMenu ())
{
inPopup = true;
auto self = shared (this);
platformMenu->popup (this, [self, callback] (COptionMenu* menu, PlatformOptionMenuResult result) {
if (result.menu != nullptr)
{
bool preventSettingValue = false;
if (self->listeners)
{
self->listeners->forEach (
[self, &result] (IOptionMenuListener* l) {
return l->onOptionMenuSetPopupResult (self, result.menu,
result.index);
},
[&preventSettingValue] (bool result) {
if (result)
preventSettingValue = true;
return result;
});
}
if (!preventSettingValue)
{
self->beginEdit ();
self->lastMenu = result.menu;
self->lastResult = result.index;
self->lastMenu->setValue (static_cast<float> (self->lastResult));
self->valueChanged ();
self->invalid ();
if (auto commandItem = dynamic_cast<CCommandMenuItem*> (
self->lastMenu->getEntry (self->lastResult)))
commandItem->execute ();
self->endEdit ();
}
}
self->afterPopup ();
if (callback)
callback (self);
self->inPopup = false;
});
}
}
return true;
}
//------------------------------------------------------------------------
bool COptionMenu::popup (CFrame* frame, const CPoint& frameLocation, const PopupCallback& callback)
{
if (frame == nullptr || menuItems->empty ())
return false;
if (isAttached ())
return false;
CView* oldFocusView = frame->getFocusView ();
CRect size (frameLocation, CPoint (0, 0));
setViewSize (size);
frame->addView (this);
auto prevFocusView = shared (oldFocusView);
popup ([prevFocusView, callback] (COptionMenu* menu) {
if (auto frame = menu->getFrame ())
{
frame->removeView (menu, false);
frame->setFocusView (prevFocusView);
}
else
{
// if the selected menu item is a command menu and the command menu has removed this
// option menu from the view hierarchy then we have to make sure the reference count is
// corrected
menu->remember ();
}
if (callback)
callback (menu);
});
return true;
}
//------------------------------------------------------------------------
void COptionMenu::cleanupSeparators (bool deep)
{
if (getItems ()->empty ())
return;
std::list<int32_t>indicesToRemove;
bool lastEntryWasSeparator = true;
for (auto i = 0; i < getNbEntries () - 1; ++i)
{
auto entry = getEntry (i);
vstgui_assert (entry);
if (!entry)
continue;
if (entry->isSeparator ())
{
if (lastEntryWasSeparator)
{
indicesToRemove.push_front (i);
}
lastEntryWasSeparator = true;
}
else
lastEntryWasSeparator = false;
if (deep)
{
if (auto subMenu = entry->getSubmenu ())
{
subMenu->cleanupSeparators (deep);
}
}
}
auto lastIndex = getNbEntries () - 1;
if (getEntry (lastIndex)->isSeparator ())
{
indicesToRemove.push_front (lastIndex);
}
for (auto index : indicesToRemove)
{
removeEntry (index);
}
}
//------------------------------------------------------------------------
void COptionMenu::setPrefixNumbers (int32_t preCount)
{
if (preCount >= 0 && preCount <= 4)
prefixNumbers = preCount;
}
/**
* @param item menu item to add. Takes ownership of item.
* @param index position of insertation. -1 appends the item
*/
//-----------------------------------------------------------------------------
CMenuItem* COptionMenu::addEntry (CMenuItem* item, int32_t index)
{
if (index < 0 || index > getNbEntries ())
menuItems->emplace_back (owned (item));
else
{
menuItems->insert (menuItems->begin () + index, owned (item));
}
return item;
}
//-----------------------------------------------------------------------------
CMenuItem* COptionMenu::addEntry (COptionMenu* submenu, const UTF8String& title)
{
auto* item = new CMenuItem (title, submenu);
return addEntry (item);
}
//-----------------------------------------------------------------------------
CMenuItem* COptionMenu::addEntry (const UTF8String& title, int32_t index, int32_t itemFlags)
{
if (title == "-")
return addSeparator (index);
auto* item = new CMenuItem (title, nullptr, 0, nullptr, itemFlags);
return addEntry (item, index);
}
//-----------------------------------------------------------------------------
CMenuItem* COptionMenu::addSeparator (int32_t index)
{
auto* item = new CMenuItem ("", nullptr, 0, nullptr, CMenuItem::kSeparator);
return addEntry (item, index);
}
//-----------------------------------------------------------------------------
CMenuItem* COptionMenu::getCurrent () const
{
return getEntry (currentIndex);
}
//-----------------------------------------------------------------------------
CMenuItem* COptionMenu::getEntry (int32_t index) const
{
if (index < 0 || menuItems->empty () || index >= getNbEntries ())
return nullptr;
return (*menuItems)[static_cast<size_t> (index)];
}
//-----------------------------------------------------------------------------
int32_t COptionMenu::getNbEntries () const
{
return static_cast<int32_t> (menuItems->size ());
}
//------------------------------------------------------------------------
COptionMenu* COptionMenu::getSubMenu (int32_t idx) const
{
CMenuItem* item = getEntry (idx);
if (item)
return item->getSubmenu ();
return nullptr;
}
//------------------------------------------------------------------------
int32_t COptionMenu::getCurrentIndex (bool countSeparator) const
{
if (countSeparator)
return currentIndex;
int32_t i = 0;
int32_t numSeparators = 0;
for (auto& item : *menuItems)
{
if (item->isSeparator ())
numSeparators++;
if (i == currentIndex)
break;
i++;
}
return currentIndex - numSeparators;
}
//------------------------------------------------------------------------
bool COptionMenu::setCurrent (int32_t index, bool countSeparator)
{
CMenuItem* item = nullptr;
if (countSeparator)
{
item = getEntry (index);
if (!item || item->isSeparator ())
return false;
currentIndex = index;
}
else
{
int32_t i = 0;
for (auto& menuItem : *menuItems)
{
if (i > index)
break;
if (menuItem->isSeparator ())
index++;
i++;
}
currentIndex = index;
item = getEntry (currentIndex);
}
if (item && style & (kMultipleCheckStyle & ~kCheckStyle))
item->setChecked (!item->isChecked ());
// to force the redraw
setDirty ();
return true;
}
//------------------------------------------------------------------------
bool COptionMenu::removeEntry (int32_t index)
{
if (index < 0 || menuItems->empty () || index >= getNbEntries ())
return false;
menuItems->erase (menuItems->begin () + index);
return true;
}
//------------------------------------------------------------------------
bool COptionMenu::removeAllEntry ()
{
menuItems->clear ();
return true;
}
//------------------------------------------------------------------------
bool COptionMenu::checkEntry (int32_t index, bool state)
{
CMenuItem* item = getEntry (index);
if (item)
{
item->setChecked (state);
return true;
}
return false;
}
//------------------------------------------------------------------------
bool COptionMenu::checkEntryAlone (int32_t index)
{
int32_t pos = 0;
for (auto& item : *menuItems)
{
item->setChecked (pos == index);
pos++;
}
return true;
}
//------------------------------------------------------------------------
bool COptionMenu::isCheckEntry (int32_t index) const
{
CMenuItem* item = getEntry (index);
if (item && item->isChecked ())
return true;
return false;
}
//------------------------------------------------------------------------
void COptionMenu::draw (CDrawContext *pContext)
{
CMenuItem* item = getEntry (currentIndex);
drawBack (pContext, inPopup ? bgWhenClick : nullptr);
if (item)
drawPlatformText (pContext, item->getTitle ());
setDirty (false);
}
//------------------------------------------------------------------------
CMouseEventResult COptionMenu::onMouseDown (CPoint& where, const CButtonState& buttons)
{
lastButton = buttons;
if (lastButton & (kLButton|kRButton|kApple))
{
auto self = shared (this);
getFrame ()->doAfterEventProcessing ([self] () {
self->doPopup ();
});
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
COptionMenu *COptionMenu::getLastItemMenu (int32_t &idxInMenu) const
{
idxInMenu = lastMenu ? (int32_t)lastMenu->getValue (): -1;
return lastMenu;
}
//------------------------------------------------------------------------
void COptionMenu::setValue (float val)
{
auto newIndex = static_cast<int32_t> (std::round (val));
if (newIndex < 0 || newIndex >= getNbEntries ())
return;
currentIndex = newIndex;
if (style & (kMultipleCheckStyle & ~kCheckStyle))
{
CMenuItem* item = getCurrent ();
if (item)
item->setChecked (!item->isChecked ());
}
CParamDisplay::setValue (static_cast<float> (newIndex));
// to force the redraw
setDirty ();
}
//------------------------------------------------------------------------
float COptionMenu::getMax () const
{
if (menuItems->empty ())
return 0.f;
return static_cast<float> (menuItems->size () - 1);
}
//------------------------------------------------------------------------
void COptionMenu::takeFocus ()
{
CParamDisplay::takeFocus ();
}
//------------------------------------------------------------------------
void COptionMenu::looseFocus ()
{
CView* receiver = getParentView () ? getParentView () : getFrame ();
while (receiver)
{
if (receiver->notify (this, kMsgLooseFocus) == kMessageNotified)
break;
receiver = receiver->getParentView ();
}
CParamDisplay::looseFocus ();
}
} // VSTGUI
@@ -0,0 +1,339 @@
// 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
#pragma once
#include "cparamdisplay.h"
#include "icommandmenuitemtarget.h"
#include "ioptionmenulistener.h"
#include "../cstring.h"
#include "../dispatchlist.h"
#include "../cbitmap.h"
#include <vector>
#include <functional>
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CMenuItem Declaration
//! @brief a menu item
//-----------------------------------------------------------------------------
class CMenuItem : public CBaseObject
{
public:
enum Flags {
kNoFlags = 0,
/** item is gray and not selectable */
kDisabled = 1 << 0,
/** item indicates a title and is not selectable */
kTitle = 1 << 1,
/** item has a checkmark */
kChecked = 1 << 2,
/** item is a separator */
kSeparator = 1 << 3
};
CMenuItem (const UTF8String& title, const UTF8String& keycode = "", int32_t keyModifiers = 0, CBitmap* icon = nullptr, int32_t flags = kNoFlags);
CMenuItem (const UTF8String& title, COptionMenu* submenu, CBitmap* icon = nullptr);
CMenuItem (const UTF8String& title, int32_t tag);
CMenuItem (const CMenuItem& item);
//-----------------------------------------------------------------------------
/// @name CMenuItem Methods
//-----------------------------------------------------------------------------
//@{
/** set title of menu item */
virtual void setTitle (const UTF8String& title);
/** set submenu of menu item */
virtual void setSubmenu (COptionMenu* submenu);
/** set keycode and key modifiers of menu item */
virtual void setKey (const UTF8String& keyCode, int32_t keyModifiers = 0);
/** set virtual key and key modifiers of menu item */
virtual void setVirtualKey (VirtualKey virtualKey, int32_t keyModifiers = 0);
/** set menu item enabled state */
virtual void setEnabled (bool state = true);
/** set menu item checked state */
virtual void setChecked (bool state = true);
/** set menu item title state */
virtual void setIsTitle (bool state = true);
/** set menu item separator state */
virtual void setIsSeparator (bool state = true);
/** set menu item icon */
virtual void setIcon (CBitmap* icon);
/** set menu item tag */
virtual void setTag (int32_t tag);
/** returns whether the item is enabled or not */
bool isEnabled () const;
/** returns whether the item is checked or not */
bool isChecked () const;
/** returns whether the item is a title item or not */
bool isTitle () const;
/** returns whether the item is a separator or not */
bool isSeparator () const;
/** returns the title of the item */
const UTF8String& getTitle () const;
/** returns the key modifiers of the item */
int32_t getKeyModifiers () const;
/** returns the keycode of the item */
const UTF8String& getKeycode () const;
/** returns the virtual key of the item */
VirtualKey getVirtualKey () const;
/** returns the submenu of the item */
COptionMenu* getSubmenu () const;
/** returns the icon of the item */
CBitmap* getIcon () const;
/** returns the tag of the item */
int32_t getTag () const;
//@}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
int32_t getVirtualKeyCode () const;
virtual void setVirtualKey (int32_t virtualKeyCode, int32_t keyModifiers = 0);
#endif
//------------------------------------------------------------------------
protected:
CMenuItem ();
~CMenuItem () noexcept override;
struct Impl;
std::unique_ptr<Impl> impl;
};
//-----------------------------------------------------------------------------
// CCommandMenuItem Declaration
/// @brief a command menu item
/// @ingroup new_in_4_1
//-----------------------------------------------------------------------------
class CCommandMenuItem : public CMenuItem
{
public:
struct Desc
{
UTF8String title;
UTF8String commandCategory;
UTF8String commandName;
UTF8String keycode;
SharedPointer<ICommandMenuItemTarget> target;
SharedPointer<CBitmap> icon;
int32_t keyModifiers {0};
int32_t flags {kNoFlags};
int32_t tag {-1};
Desc () = default;
~Desc () noexcept = default;
Desc (const UTF8String& title, const UTF8String& keycode = nullptr,
int32_t keyModifiers = 0, CBitmap* icon = nullptr,
int32_t flags = kNoFlags, ICommandMenuItemTarget* target = nullptr,
const UTF8String& commandCategory = nullptr,
const UTF8String& commandName = nullptr)
: title (title)
, commandCategory (commandCategory)
, commandName (commandName)
, keycode (keycode)
, target (target)
, icon (icon)
, keyModifiers (keyModifiers)
, flags (flags)
{
}
Desc (const UTF8String& title, int32_t tag, ICommandMenuItemTarget* target = nullptr,
const UTF8String& commandCategory = nullptr, const UTF8String& commandName = nullptr)
: title (title)
, commandCategory (commandCategory)
, commandName (commandName)
, target (target)
, tag (tag)
{
}
Desc (const UTF8String& title, ICommandMenuItemTarget* target,
const UTF8String& commandCategory = nullptr,
const UTF8String& commandName = nullptr)
: title (title)
, commandCategory (commandCategory)
, commandName (commandName)
, target (target)
{
}
};
CCommandMenuItem (Desc&& args);
CCommandMenuItem (const Desc& args);
CCommandMenuItem (const CCommandMenuItem& item);
~CCommandMenuItem () noexcept override = default;
//-----------------------------------------------------------------------------
/// @name CCommandMenuItem Methods
//-----------------------------------------------------------------------------
//@{
void setCommandCategory (const UTF8String& category);
const UTF8String& getCommandCategory () const { return commandCategory; }
bool isCommandCategory (const UTF8String& category) const;
void setCommandName (const UTF8String& name);
const UTF8String& getCommandName () const { return commandName; }
bool isCommandName (const UTF8String& name) const;
void setItemTarget (ICommandMenuItemTarget* target);
ICommandMenuItemTarget* getItemTarget () const { return itemTarget; }
using ValidateCallbackFunction = std::function<void(CCommandMenuItem* item)>;
using SelectedCallbackFunction = std::function<void(CCommandMenuItem* item)>;
void setActions (SelectedCallbackFunction&& selected, ValidateCallbackFunction&& validate = [](CCommandMenuItem*){});
//@}
void execute ();
void validate ();
protected:
ValidateCallbackFunction validateFunc;
SelectedCallbackFunction selectedFunc;
UTF8String commandCategory;
UTF8String commandName;
SharedPointer<ICommandMenuItemTarget> itemTarget;
};
using CMenuItemList = std::vector<SharedPointer<CMenuItem>>;
using CMenuItemIterator = CMenuItemList::iterator;
using CConstMenuItemIterator = CMenuItemList::const_iterator;
//-----------------------------------------------------------------------------
// COptionMenu Declaration
//! @brief a popup menu control
/// @ingroup controls
//-----------------------------------------------------------------------------
class COptionMenu : public CParamDisplay
{
private:
enum StyleEnum
{
StylePopup = CParamDisplay::LastStyle,
StyleCheck,
StyleMultipleCheck,
};
public:
COptionMenu ();
COptionMenu (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background = nullptr, CBitmap* bgWhenClick = nullptr, const int32_t style = 0);
COptionMenu (const COptionMenu& menu);
~COptionMenu () noexcept override;
enum Style
{
kPopupStyle = 1 << StylePopup,
kCheckStyle = 1 << StyleCheck,
kMultipleCheckStyle = 1 << StyleMultipleCheck
};
bool isPopupStyle () const { return hasBit (getStyle (), kPopupStyle); }
bool isCheckStyle () const { return hasBit (getStyle (), kCheckStyle); }
bool isMultipleCheckStyle () const { return hasBit (getStyle (), kMultipleCheckStyle); }
//-----------------------------------------------------------------------------
/// @name COptionMenu Methods
//-----------------------------------------------------------------------------
//@{
/** add a new entry */
virtual CMenuItem* addEntry (CMenuItem* item, int32_t index = -1);
/** add a new submenu entry */
virtual CMenuItem* addEntry (COptionMenu* submenu, const UTF8String& title);
/** add a new entry */
virtual CMenuItem* addEntry (const UTF8String& title, int32_t index = -1, int32_t itemFlags = CMenuItem::kNoFlags);
/** add a new separator entry */
virtual CMenuItem* addSeparator (int32_t index = -1);
/** get current entry */
virtual CMenuItem* getCurrent () const;
/** TODO: Doc */
virtual int32_t getCurrentIndex (bool countSeparator = false) const;
/** get entry at index position */
virtual CMenuItem* getEntry (int32_t index) const;
/** get number of entries */
virtual int32_t getNbEntries () const;
/** set current entry */
virtual bool setCurrent (int32_t index, bool countSeparator = true);
/** remove an entry */
virtual bool removeEntry (int32_t index);
/** remove all entries */
virtual bool removeAllEntry ();
/** change check state of entry at index */
virtual bool checkEntry (int32_t index, bool state);
/** check entry at index and uncheck every other item */
virtual bool checkEntryAlone (int32_t index);
/** get check state of entry at index */
virtual bool isCheckEntry (int32_t index) const;
/** Windows only */
virtual void setNbItemsPerColumn (int32_t val) { nbItemsPerColumn = val; }
/** Windows only */
virtual int32_t getNbItemsPerColumn () const { return nbItemsPerColumn; }
/** get last index of choosen entry */
int32_t getLastResult () const { return lastResult; }
/** get last menu and index of choosen entry */
COptionMenu* getLastItemMenu (int32_t& idxInMenu) const;
/** set prefix numbering */
virtual void setPrefixNumbers (int32_t preCount);
/** get prefix numbering */
int32_t getPrefixNumbers () const { return prefixNumbers; }
/** get a submenu */
COptionMenu* getSubMenu (int32_t idx) const;
/** popup callback function */
using PopupCallback = std::function<void (COptionMenu* menu)>;
/** pops up the menu */
bool popup (const PopupCallback& callback = {});
/** pops up the menu at frameLocation */
bool popup (CFrame* frame, const CPoint& frameLocation, const PopupCallback& callback = {});
CMenuItemList* getItems () const { return menuItems; }
/** remove separators as first and last item and double separators */
void cleanupSeparators (bool deep);
void registerOptionMenuListener (IOptionMenuListener* listener);
void unregisterOptionMenuListener (IOptionMenuListener* listener);
//@}
// overrides
void setValue (float val) override;
void setMin (float val) override {}
float getMin () const override { return 0; }
void setMax (float val) override {}
float getMax () const override;
void draw (CDrawContext* pContext) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
void onKeyboardEvent (KeyboardEvent& event) override;
void takeFocus () override;
void looseFocus () override;
CLASS_METHODS(COptionMenu, CParamDisplay)
protected:
bool doPopup ();
void beforePopup ();
void afterPopup ();
CMenuItemList* menuItems;
bool inPopup {false};
int32_t currentIndex {-1};
CButtonState lastButton {0};
int32_t nbItemsPerColumn {-1};
int32_t lastResult {-1};
int32_t prefixNumbers {0};
SharedPointer<CBitmap> bgWhenClick;
COptionMenu* lastMenu {nullptr};
using MenuListenerList = DispatchList<IOptionMenuListener*>;
std::unique_ptr<MenuListenerList> listeners;
};
} // VSTGUI
@@ -0,0 +1,504 @@
// 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 "cparamdisplay.h"
#include "../cbitmap.h"
#include "../cframe.h"
#include "../cstring.h"
#include "../cgraphicspath.h"
#include "../cdrawcontext.h"
#include <string>
namespace VSTGUI {
//------------------------------------------------------------------------
// CParamDisplay
//------------------------------------------------------------------------
/*! @class CParamDisplay
Define a rectangle view where a text-value can be displayed with a given font and color.
The user can specify its convert function (from float to char) by default the string format is "%2.2f".
The text-value is centered in the given rect.
*/
CParamDisplay::CParamDisplay (const CRect& size, CBitmap* background, int32_t inStyle)
: CControl (size, nullptr, -1, background)
, horiTxtAlign (kCenterText)
, style (inStyle)
, valuePrecision (2)
, roundRectRadius (6.)
, frameWidth (1.)
, textRotation (0.)
{
setBit (style, kAntialias, true);
backOffset (0, 0);
fontID = kNormalFont; fontID->remember ();
fontColor = kWhiteCColor;
backColor = kBlackCColor;
frameColor = kBlackCColor;
shadowColor = kRedCColor;
if (hasBit (style, kNoDrawStyle))
setDirty (false);
}
//------------------------------------------------------------------------
CParamDisplay::CParamDisplay (const CParamDisplay& v)
: CControl (v)
, valueToStringFunction (v.valueToStringFunction)
, horiTxtAlign (v.horiTxtAlign)
, style (v.style)
, valuePrecision (v.valuePrecision)
, fontID (v.fontID)
, fontColor (v.fontColor)
, backColor (v.backColor)
, frameColor (v.frameColor)
, shadowColor (v.shadowColor)
, textInset (v.textInset)
, backOffset (v.backOffset)
, roundRectRadius (v.roundRectRadius)
, frameWidth (v.frameWidth)
, textRotation (v.textRotation)
{
fontID->remember ();
}
//------------------------------------------------------------------------
CParamDisplay::~CParamDisplay () noexcept
{
if (fontID)
fontID->forget ();
}
//------------------------------------------------------------------------
bool CParamDisplay::removed (CView* parent)
{
return CControl::removed (parent);
}
//------------------------------------------------------------------------
void CParamDisplay::setStyle (int32_t val)
{
setBit (val, kAntialias, hasBit (style, kAntialias));
if (style != val)
{
style = val;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
int32_t CParamDisplay::getStyle () const
{
auto tmp = style;
setBit (tmp, kAntialias, false);
return tmp;
}
//------------------------------------------------------------------------
void CParamDisplay::setPrecision (uint8_t precision)
{
if (valuePrecision != precision)
{
valuePrecision = precision;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setValueToStringFunction2 (const ValueToStringFunction2& valueToStringFunc)
{
valueToStringFunction = valueToStringFunc;
}
//------------------------------------------------------------------------
void CParamDisplay::setValueToStringFunction2 (ValueToStringFunction2&& valueToStringFunc)
{
valueToStringFunction = std::move (valueToStringFunc);
}
//------------------------------------------------------------------------
void CParamDisplay::setValueToStringFunction (const ValueToStringFunction& func)
{
if (!func)
{
setValueToStringFunction2 (nullptr);
return;
}
setValueToStringFunction2 ([=] (float value, std::string& str, CParamDisplay* display) {
char string[256];
string[0] = 0;
if (func (value, string, display))
{
str = string;
return true;
}
return false;
});
}
//------------------------------------------------------------------------
void CParamDisplay::setValueToStringFunction (ValueToStringFunction&& func)
{
setValueToStringFunction (func);
}
//------------------------------------------------------------------------
bool CParamDisplay::getFocusPath (CGraphicsPath& outPath)
{
if (wantsFocus ())
{
auto lineWidth = getFrameWidth ();
if (lineWidth < 0.)
lineWidth = 1.;
CCoord focusWidth = getFrame ()->getFocusWidth ();
CRect r (getViewSize ());
if (hasBit (style, kRoundRectStyle))
{
r.inset (lineWidth / 2., lineWidth / 2.);
outPath.addRoundRect (r, roundRectRadius);
outPath.closeSubpath ();
r.extend (focusWidth, focusWidth);
outPath.addRoundRect (r, roundRectRadius);
}
else
{
r.inset (lineWidth / 2., lineWidth / 2.);
outPath.addRect (r);
r.extend (focusWidth, focusWidth);
outPath.addRect (r);
}
}
return true;
}
//------------------------------------------------------------------------
void CParamDisplay::draw (CDrawContext *pContext)
{
if (hasBit (style, kNoDrawStyle))
return;
std::string string;
bool converted = false;
if (valueToStringFunction)
converted = valueToStringFunction (value, string, this);
if (!converted)
{
char tmp[255];
char precisionStr[10];
snprintf (precisionStr, 10, "%%.%hhuf", valuePrecision);
snprintf (tmp, 255, precisionStr, value);
string = tmp;
}
drawBack (pContext);
drawPlatformText (pContext, UTF8String (string));
setDirty (false);
}
//------------------------------------------------------------------------
void CParamDisplay::drawBack (CDrawContext* pContext, CBitmap* newBack)
{
pContext->setDrawMode (kAliasing);
auto lineWidth = getFrameWidth ();
if (lineWidth < 0.)
lineWidth = pContext->getHairlineSize ();
if (newBack)
{
newBack->draw (pContext, getViewSize (), backOffset);
}
else if (getDrawBackground ())
{
getDrawBackground ()->draw (pContext, getViewSize (), backOffset);
}
else
{
if (!getTransparency ())
{
bool strokePath = !(hasBit (style, (k3DIn|k3DOut|kNoFrame)));
pContext->setFillColor (backColor);
if (hasBit (style, kRoundRectStyle))
{
CRect pathRect = getViewSize ();
pathRect.inset (lineWidth/2., lineWidth/2.);
SharedPointer<CGraphicsPath> path = owned (pContext->createRoundRectGraphicsPath (pathRect, roundRectRadius));
if (path)
{
pContext->setDrawMode (kAntiAliasing);
pContext->drawGraphicsPath (path, CDrawContext::kPathFilled);
if (strokePath)
{
pContext->setLineStyle (kLineSolid);
pContext->setLineWidth (lineWidth);
pContext->setFrameColor (frameColor);
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
}
}
else
{
pContext->setDrawMode (kAntiAliasing);
SharedPointer<CGraphicsPath> path = owned (pContext->createGraphicsPath ());
if (path)
{
CRect frameRect = getViewSize ();
if (strokePath)
frameRect.inset (lineWidth/2., lineWidth/2.);
path->addRect (frameRect);
pContext->drawGraphicsPath (path, CDrawContext::kPathFilled);
if (strokePath)
{
pContext->setLineStyle (kLineSolid);
pContext->setLineWidth (lineWidth);
pContext->setFrameColor (frameColor);
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
}
else
{
pContext->drawRect (getViewSize (), kDrawFilled);
if (strokePath)
{
CRect frameRect = getViewSize ();
frameRect.inset (lineWidth/2., lineWidth/2.);
pContext->setLineStyle (kLineSolid);
pContext->setLineWidth (lineWidth);
pContext->setFrameColor (frameColor);
pContext->drawRect (frameRect);
}
}
}
}
}
// draw the frame for the 3D effect
if (hasBit (style, (k3DIn|k3DOut)))
{
CRect r (getViewSize ());
r.inset (lineWidth/2., lineWidth/2.);
pContext->setDrawMode (kAliasing);
pContext->setLineWidth (lineWidth);
pContext->setLineStyle (kLineSolid);
if (hasBit (style, k3DIn))
pContext->setFrameColor (backColor);
else
pContext->setFrameColor (frameColor);
CPoint p;
SharedPointer<CGraphicsPath> path = owned (pContext->createGraphicsPath ());
if (path)
{
path->beginSubpath (p (r.left, r.bottom));
path->addLine (p (r.left, r.top));
path->addLine (p (r.right, r.top));
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
else
{
pContext->drawLine (CPoint (r.left, r.bottom), CPoint (r.left, r.top));
pContext->drawLine (CPoint (r.left, r.top), CPoint (r.right, r.top));
}
if (hasBit (style, k3DIn))
pContext->setFrameColor (frameColor);
else
pContext->setFrameColor (backColor);
path = owned (pContext->createGraphicsPath ());
if (path)
{
path->beginSubpath (p (r.right, r.top));
path->addLine (p (r.right, r.bottom));
path->addLine (p (r.left, r.bottom));
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
else
{
pContext->drawLine (CPoint (r.right, r.top), CPoint (r.right, r.bottom));
pContext->drawLine (CPoint (r.right, r.bottom), CPoint (r.left, r.bottom));
}
}
}
//------------------------------------------------------------------------
void CParamDisplay::drawPlatformText (CDrawContext* pContext, const UTF8String& string)
{
drawPlatformText (pContext, string, getViewSize ());
}
//------------------------------------------------------------------------
void CParamDisplay::drawPlatformText (CDrawContext* pContext, const UTF8String& string,
const CRect& size)
{
if (!hasBit (style, kNoTextStyle))
{
pContext->saveGlobalState ();
CRect textRect (size);
textRect.inset (textInset.x, textInset.y);
drawClipped (pContext, textRect, [&] () {
CPoint center (textRect.getCenter ());
CGraphicsTransform transform;
transform.rotate (textRotation, center);
CDrawContext::Transform ctxTransform (*pContext, transform);
pContext->setDrawMode (kAntiAliasing);
pContext->setFont (fontID);
// draw darker text (as shadow)
if (hasBit (style, kShadowText))
{
CRect newSize (textRect);
newSize.offset (shadowTextOffset);
pContext->setFontColor (shadowColor);
pContext->drawString (string.getPlatformString (), newSize, horiTxtAlign,
hasBit (style, kAntialias));
}
pContext->setFontColor (fontColor);
pContext->drawString (string.getPlatformString (), textRect, horiTxtAlign,
hasBit (style, kAntialias));
});
pContext->restoreGlobalState ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setFont (CFontRef inFontID)
{
if (fontID)
fontID->forget ();
fontID = inFontID;
if (fontID)
fontID->remember ();
drawStyleChanged ();
}
//------------------------------------------------------------------------
void CParamDisplay::setFontColor (CColor color)
{
// to force the redraw
if (fontColor != color)
{
fontColor = color;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setBackColor (CColor color)
{
// to force the redraw
if (backColor != color)
{
backColor = color;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setFrameColor (CColor color)
{
// to force the redraw
if (frameColor != color)
{
frameColor = color;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setShadowColor (CColor color)
{
// to force the redraw
if (shadowColor != color)
{
shadowColor = color;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setShadowTextOffset (const CPoint& offset)
{
if (shadowTextOffset != offset)
{
shadowTextOffset = offset;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setHoriAlign (CHoriTxtAlign hAlign)
{
// to force the redraw
if (horiTxtAlign != hAlign)
{
horiTxtAlign = hAlign;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setTextInset (const CPoint& p)
{
if (textInset != p)
{
textInset = p;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setTextRotation (double angle)
{
while (angle < 0.)
angle += 360.;
while (angle > 360.)
angle -= 360.;
if (textRotation != angle)
{
textRotation = angle;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setRoundRectRadius (const CCoord& radius)
{
if (roundRectRadius != radius)
{
roundRectRadius = radius;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::setFrameWidth (const CCoord& width)
{
if (frameWidth != width)
{
frameWidth = width;
drawStyleChanged ();
}
}
//------------------------------------------------------------------------
void CParamDisplay::drawStyleChanged ()
{
setDirty ();
}
//------------------------------------------------------------------------
void CParamDisplay::setBackOffset (const CPoint &offset)
{
backOffset = offset;
}
//-----------------------------------------------------------------------------
void CParamDisplay::copyBackOffset ()
{
backOffset (getViewSize ().left, getViewSize ().top);
}
} // VSTGUI
@@ -0,0 +1,154 @@
// 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
#pragma once
#include "ccontrol.h"
#include "../cfont.h"
#include "../ccolor.h"
#include "../cdrawdefs.h"
#include <functional>
namespace VSTGUI {
//-----------------------------------------------------------------------------
using CParamDisplayValueToStringProc = bool (*) (float value, char utf8String[256], void* userData);
//-----------------------------------------------------------------------------
// CParamDisplay Declaration
//! @brief a parameter display
/// @ingroup views
//-----------------------------------------------------------------------------
class CParamDisplay : public CControl
{
protected:
enum StyleEnum
{
StyleShadowText = 0,
Style3DIn,
Style3DOut,
StyleNoText,
StyleNoDraw,
StyleRoundRect,
StyleNoFrame,
StyleAntialias,
LastStyle
};
public:
CParamDisplay (const CRect& size, CBitmap* background = nullptr, int32_t style = 0);
CParamDisplay (const CParamDisplay& paramDisplay);
//-----------------------------------------------------------------------------
/// @name CParamDisplay Methods
//-----------------------------------------------------------------------------
//@{
virtual void setFont (CFontRef fontID);
const CFontRef getFont () const { return fontID; }
virtual void setFontColor (CColor color);
CColor getFontColor () const { return fontColor; }
virtual void setBackColor (CColor color);
CColor getBackColor () const { return backColor; }
virtual void setFrameColor (CColor color);
CColor getFrameColor () const { return frameColor; }
virtual void setShadowColor (CColor color);
CColor getShadowColor () const { return shadowColor; }
virtual void setShadowTextOffset (const CPoint& offset);
CPoint getShadowTextOffset () const { return shadowTextOffset; }
virtual void setAntialias (bool state) { setBit (style, kAntialias, state); }
bool getAntialias () const { return hasBit (style, kAntialias); }
virtual void setHoriAlign (CHoriTxtAlign hAlign);
CHoriTxtAlign getHoriAlign () const { return horiTxtAlign; }
virtual void setTextInset (const CPoint& p);
CPoint getTextInset () const { return textInset; }
virtual void setTextRotation (double angle);
double getTextRotation () const { return textRotation; }
virtual void setRoundRectRadius (const CCoord& radius);
CCoord getRoundRectRadius () const { return roundRectRadius; }
virtual void setFrameWidth (const CCoord& width);
CCoord getFrameWidth () const { return frameWidth; }
using ValueToStringUserData = CParamDisplay;
using ValueToStringFunction = std::function<bool (float value, char utf8String[256], CParamDisplay* display)>;
void setValueToStringFunction (const ValueToStringFunction& valueToStringFunc);
void setValueToStringFunction (ValueToStringFunction&& valueToStringFunc);
using ValueToStringFunction2 = std::function<bool (float value, std::string& result, CParamDisplay* display)>;
void setValueToStringFunction2 (const ValueToStringFunction2& valueToStringFunc);
void setValueToStringFunction2 (ValueToStringFunction2&& valueToStringFunc);
enum Style
{
kShadowText = 1 << StyleShadowText,
k3DIn = 1 << Style3DIn,
k3DOut = 1 << Style3DOut,
kNoTextStyle = 1 << StyleNoText,
kNoDrawStyle = 1 << StyleNoDraw,
kRoundRectStyle = 1 << StyleRoundRect,
kNoFrame = 1 << StyleNoFrame,
};
virtual void setStyle (int32_t val);
int32_t getStyle () const;
virtual void setPrecision (uint8_t precision);
uint8_t getPrecision () const { return valuePrecision; }
virtual void setBackOffset (const CPoint& offset);
const CPoint& getBackOffset () const { return backOffset; }
void copyBackOffset ();
//@}
void draw (CDrawContext* pContext) override;
bool getFocusPath (CGraphicsPath& outPath) override;
bool removed (CView* parent) override;
CLASS_METHODS(CParamDisplay, CControl)
protected:
~CParamDisplay () noexcept override;
virtual void drawBack (CDrawContext* pContext, CBitmap* newBack = nullptr);
virtual void drawPlatformText (CDrawContext* pContext, const UTF8String& string);
virtual void drawPlatformText (CDrawContext* pContext, const UTF8String& string,
const CRect& size);
virtual void drawStyleChanged ();
ValueToStringFunction2 valueToStringFunction;
enum StylePrivate {
kAntialias = 1 << StyleAntialias,
};
CHoriTxtAlign horiTxtAlign;
int32_t style;
uint8_t valuePrecision;
CFontRef fontID;
CColor fontColor;
CColor backColor;
CColor frameColor;
CColor shadowColor;
CPoint textInset;
CPoint shadowTextOffset {1., 1.};
CPoint backOffset;
CCoord roundRectRadius;
CCoord frameWidth;
double textRotation;
};
} // VSTGUI
@@ -0,0 +1,416 @@
// 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 "cscrollbar.h"
#include "../cvstguitimer.h"
#include "../animation/animations.h"
#include "../animation/timingfunctions.h"
#include "../cframe.h"
#include "../cgraphicspath.h"
#include "../cdrawcontext.h"
#include "../events.h"
#include "../algorithm.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
CScrollbar::CScrollbar (const CRect& size, IControlListener* listener, int32_t tag, ScrollbarDirection direction, const CRect& scrollSize)
: CControl (size, listener, tag, nullptr)
, direction (direction)
, scrollSize (scrollSize)
, scrollerArea (size)
, stepValue (0.1f)
, scrollerLength (0)
, overlayStyle (false)
, mouseIsInside (false)
, drawer (nullptr)
{
setTransparency (true);
setWheelInc (0.05f);
scrollerArea.inset (2, 2);
calculateScrollerLength ();
frameColor (0, 0, 0, 255);
scrollerColor (0, 0, 255, 255);
backgroundColor (255, 255, 255, 200);
}
//-----------------------------------------------------------------------------
CScrollbar::CScrollbar (const CScrollbar& v)
: CControl (v)
, direction (v.direction)
, scrollSize (v.scrollSize)
, scrollerArea (v.scrollerArea)
, stepValue (v.stepValue)
, scrollerLength (v.scrollerLength)
, frameColor (v.frameColor)
, scrollerColor (v.scrollerColor)
, backgroundColor (v.backgroundColor)
, overlayStyle (v.overlayStyle)
, mouseIsInside (false)
, drawer (v.drawer)
{
calculateScrollerLength ();
}
//-----------------------------------------------------------------------------
void CScrollbar::setViewSize (const CRect &newSize, bool invalid)
{
scrollerArea = newSize;
scrollerArea.inset (2, 2);
CControl::setViewSize (newSize, invalid);
calculateScrollerLength ();
}
//-----------------------------------------------------------------------------
void CScrollbar::setScrollSize (const CRect& ssize)
{
if (scrollSize != ssize)
{
scrollSize = ssize;
calculateScrollerLength ();
setDirty (true);
}
}
//-----------------------------------------------------------------------------
void CScrollbar::calculateScrollerLength ()
{
CCoord newScrollerLength;
if (direction == kHorizontal)
{
double factor = scrollSize.getWidth () > 0. ? getViewSize ().getWidth () / scrollSize.getWidth () : 0.;
if (factor >= 1.f)
factor = 0;
newScrollerLength = (CCoord) (getViewSize ().getWidth () * factor);
}
else
{
double factor = scrollSize.getHeight () > 0 ? getViewSize ().getHeight () / scrollSize.getHeight () : 0.;
if (factor >= 1.f)
factor = 0;
newScrollerLength = (CCoord) (getViewSize ().getHeight () * factor);
}
if (newScrollerLength < minScrollerLenght && newScrollerLength > 0.)
newScrollerLength = minScrollerLenght;
if (newScrollerLength != scrollerLength)
{
scrollerLength = newScrollerLength;
setDirty (true);
}
}
//-----------------------------------------------------------------------------
CRect CScrollbar::getScrollerRect ()
{
CRect sr (scrollerArea);
CCoord l = (direction == kHorizontal) ? scrollerArea.getWidth () : scrollerArea.getHeight ();
CCoord scrollerOffset = (CCoord)(getValueNormalized () * (l - scrollerLength));
if (direction == kHorizontal)
{
sr.setWidth (scrollerLength);
sr.offset (scrollerOffset, 0);
}
else
{
sr.setHeight (scrollerLength);
sr.offset (0, scrollerOffset);
}
return sr;
}
//-----------------------------------------------------------------------------
void CScrollbar::doStepping ()
{
CRect sr = getScrollerRect ();
if (timer)
{
if (!getViewSize ().pointInside (startPoint) || sr.pointInside (startPoint))
return;
}
bool dir = (direction == kHorizontal && startPoint.x < sr.left) || (direction == kVertical && startPoint.y < sr.top);
float newValue = getValueNormalized ();
if (direction == kHorizontal)
{
if (dir)
newValue -= (float)scrollerLength / (float)scrollerArea.getWidth ();
else
newValue += (float)scrollerLength / (float)scrollerArea.getWidth ();
}
else
{
if (dir)
newValue -= (float)scrollerLength / (float)scrollerArea.getHeight ();
else
newValue += (float)scrollerLength / (float)scrollerArea.getHeight ();
}
newValue = clampNorm (newValue);
if (newValue != getValueNormalized ())
{
setValueNormalized (newValue);
valueChanged ();
invalid ();
}
}
//-----------------------------------------------------------------------------
CMessageResult CScrollbar::notify (CBaseObject* sender, IdStringPtr message)
{
if (message == CVSTGUITimer::kMsgTimer && timer)
{
doStepping ();
timer->setFireTime (80);
return kMessageNotified;
}
return kMessageUnknown;
}
//-----------------------------------------------------------------------------
void CScrollbar::setOverlayStyle (bool state)
{
if (overlayStyle != state)
{
overlayStyle = state;
setAlphaValue (overlayStyle ? 0.001f : 1.f);
}
}
//------------------------------------------------------------------------
void CScrollbar::setMinScrollerLength (CCoord length)
{
if (minScrollerLenght != length)
{
minScrollerLenght = length;
calculateScrollerLength ();
setDirty ();
}
}
//-----------------------------------------------------------------------------
CMouseEventResult CScrollbar::onMouseEntered (CPoint& where, const CButtonState& buttons)
{
if (overlayStyle && scrollerLength != 0)
{
addAnimation ("AlphaValueAnimation", new Animation::AlphaValueAnimation (1.f), new Animation::LinearTimingFunction (100));
}
mouseIsInside = true;
return kMouseEventNotHandled;
}
//-----------------------------------------------------------------------------
CMouseEventResult CScrollbar::onMouseExited (CPoint& where, const CButtonState& buttons)
{
if (overlayStyle && scrollerLength != 0)
{
Animation::ITimingFunction* timingFunction = nullptr;
if (getAlphaValue () == 1.f)
{
auto* interpolTimingFunction = new Animation::InterpolationTimingFunction (400);
interpolTimingFunction->addPoint (300.f/400.f, 1.f);
timingFunction = interpolTimingFunction;
}
else
timingFunction = new Animation::LinearTimingFunction (100);
addAnimation ("AlphaValueAnimation", new Animation::AlphaValueAnimation (0.001f), timingFunction);
}
mouseIsInside = false;
return kMouseEventNotHandled;
}
//-----------------------------------------------------------------------------
CMouseEventResult CScrollbar::onMouseDown (CPoint &where, const CButtonState& buttons)
{
if (buttons != kLButton || scrollerLength == 0)
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
startPoint = where;
scrollerRect = getScrollerRect ();
scrolling = scrollerRect.pointInside (where);
if (scrolling)
{
scrollerRect = getScrollerRect ();
return kMouseEventHandled;
}
else if (scrollerArea.pointInside (where))
{
doStepping ();
timer = makeOwned<CVSTGUITimer> (this, 250, true);
return kMouseEventHandled;
}
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
//-----------------------------------------------------------------------------
CMouseEventResult CScrollbar::onMouseUp (CPoint &where, const CButtonState& buttons)
{
timer = nullptr;
return kMouseEventHandled;
}
//-----------------------------------------------------------------------------
CMouseEventResult CScrollbar::onMouseMoved (CPoint &where, const CButtonState& buttons)
{
if (buttons & kLButton)
{
if (scrolling)
{
float newValue = 0.f;
CPoint newPoint (where);
newPoint.x -= startPoint.x - scrollerRect.left;
newPoint.y -= startPoint.y - scrollerRect.top;
if (direction == kHorizontal)
{
newValue = (float)((float)(newPoint.x - scrollerArea.left) / ((float)scrollerArea.getWidth () - scrollerRect.getWidth ()));
}
else
{
newValue = (float)((float)(newPoint.y - scrollerArea.top) / ((float)scrollerArea.getHeight () - scrollerRect.getHeight ()));
}
newValue = clampNorm (newValue);
if (newValue != getValueNormalized ())
{
setValueNormalized (newValue);
valueChanged ();
invalid ();
}
}
else
{
CPoint old (startPoint);
startPoint = where;
CRect scollerRect = getScrollerRect ();
if (getViewSize ().pointInside (where) && scollerRect.pointInside (old) && !scrollerRect.pointInside (startPoint))
doStepping ();
}
return kMouseEventHandled;
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CScrollbar::onMouseCancel ()
{
timer = nullptr;
return kMouseEventHandled;
}
//------------------------------------------------------------------------
void CScrollbar::onVisualChange ()
{
if (isAttached () && overlayStyle && !mouseIsInside)
{
if (scrollerLength != 0)
{
auto timingFunction = new Animation::InterpolationTimingFunction (1100);
timingFunction->addPoint (1000.f/1100.f, 0);
addAnimation ("AlphaValueAnimation", new Animation::AlphaValueAnimation (0.001f), timingFunction);
setAlphaValue (1.f);
}
else
{
removeAnimation ("AlphaValueAnimation");
setAlphaValue (0.f);
}
}
}
//------------------------------------------------------------------------
void CScrollbar::onMouseWheelEvent (MouseWheelEvent& event)
{
if (scrollerLength == 0 || !getMouseEnabled ())
return;
if (!event.modifiers.empty () && !(event.modifiers.has (ModifierKey::Shift) &&
event.flags & MouseWheelEvent::DirectionInvertedFromDevice))
return;
float distance = 0.f;
if (direction == kHorizontal)
distance = static_cast<float> (event.deltaX);
else
distance = static_cast<float> (event.deltaY);
if (distance == 0.f)
return;
if (event.flags & MouseWheelEvent::DirectionInvertedFromDevice)
distance *= -1;
float newValue = getValueNormalized ();
if (event.modifiers.has (ModifierKey::Shift))
newValue -= 0.1f * distance * getWheelInc ();
else
newValue -= distance * getWheelInc ();
newValue = clampNorm (newValue);
if (newValue != getValueNormalized ())
{
setValueNormalized (newValue);
onVisualChange ();
valueChanged ();
invalid ();
}
event.consumed = true;
}
//-----------------------------------------------------------------------------
void CScrollbar::drawBackground (CDrawContext* pContext)
{
CRect r (getViewSize ());
if (drawer)
drawer->drawScrollbarBackground (pContext, r, direction, this);
else
{
pContext->setDrawMode (kAliasing);
pContext->setLineWidth (1);
pContext->setFillColor (backgroundColor);
pContext->setFrameColor (frameColor);
pContext->setLineStyle (kLineSolid);
pContext->drawRect (r, kDrawFilledAndStroked);
}
}
//-----------------------------------------------------------------------------
void CScrollbar::drawScroller (CDrawContext* pContext, const CRect& size)
{
CRect r (size);
if (drawer)
drawer->drawScrollbarScroller (pContext, r, direction, this);
else
{
pContext->setLineWidth (1);
pContext->setFillColor (scrollerColor);
pContext->setFrameColor (frameColor);
CCoord wideness = (direction == kVertical ? getWidth() : getHeight()) / 2 - 2;
auto path = (wideness > 2) ? owned (pContext->createGraphicsPath ()) : nullptr;
if (path)
{
if (wideness > 4)
wideness = 4;
pContext->setDrawMode (kAntiAliasing|kNonIntegralMode);
path->addRoundRect (r, wideness);
pContext->drawGraphicsPath (path, CDrawContext::kPathFilled);
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
else
{
pContext->setDrawMode (kAliasing|kNonIntegralMode);
pContext->drawRect (r, kDrawFilledAndStroked);
}
}
}
//-----------------------------------------------------------------------------
void CScrollbar::draw (CDrawContext* pContext)
{
drawBackground (pContext);
if (scrollerLength > 0)
{
CRect sr = getScrollerRect ();
drawScroller (pContext, sr);
}
setDirty (false);
}
}
@@ -0,0 +1,113 @@
// 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
#pragma once
#include "ccontrol.h"
#include "../ccolor.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CScrollbar Declaration
//! @brief a scrollbar control
/// @ingroup controls
//-----------------------------------------------------------------------------
class CScrollbar : public CControl
{
public:
enum ScrollbarDirection {
kHorizontal,
kVertical
};
CScrollbar (const CRect& size, IControlListener* listener, int32_t tag, ScrollbarDirection style, const CRect& scrollSize);
CScrollbar (const CScrollbar& scrollbar);
//-----------------------------------------------------------------------------
/// @name CScrollbar Methods
//-----------------------------------------------------------------------------
//@{
virtual void setDrawer (IScrollbarDrawer* d) { drawer = d; }
virtual void setScrollSize (const CRect& ssize);
virtual void setStep (float newStep) { stepValue = newStep; }
CRect& getScrollSize (CRect& rect) const { rect = scrollSize; return rect; }
float getStep () const { return stepValue; }
virtual void setFrameColor (const CColor& color) { frameColor = color; }
virtual void setScrollerColor (const CColor& color) { scrollerColor = color; }
virtual void setBackgroundColor (const CColor& color) { backgroundColor = color; }
CColor getFrameColor () const { return frameColor; }
CColor getScrollerColor () const { return scrollerColor; }
CColor getBackgroundColor () const { return backgroundColor; }
bool getOverlayStyle () const { return overlayStyle; }
virtual void setOverlayStyle (bool state);
void setMinScrollerLength (CCoord length);
CCoord getMinScrollerLength () const { return minScrollerLenght; }
virtual void onVisualChange ();
CRect getScrollerRect ();
//@}
// overwrite
void draw (CDrawContext* pContext) override;
void onMouseWheelEvent (MouseWheelEvent& event) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
CMessageResult notify (CBaseObject* sender, IdStringPtr message) override;
void setViewSize (const CRect& newSize, bool invalid) override;
CMouseEventResult onMouseEntered (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseExited (CPoint& where, const CButtonState& buttons) override;
CLASS_METHODS(CScrollbar, CControl)
//-----------------------------------------------------------------------------
protected:
~CScrollbar () noexcept override = default;
void drawBackground (CDrawContext* pContext);
void drawScroller (CDrawContext* pContext, const CRect& size);
void calculateScrollerLength ();
void doStepping ();
ScrollbarDirection direction;
CRect scrollSize;
CRect scrollerArea;
float stepValue;
CCoord scrollerLength;
CCoord minScrollerLenght {8.0};
CColor frameColor;
CColor scrollerColor;
CColor backgroundColor;
bool overlayStyle;
bool mouseIsInside;
IScrollbarDrawer* drawer;
private:
SharedPointer<CVSTGUITimer> timer;
CPoint startPoint;
CRect scrollerRect;
bool scrolling;
};
//-----------------------------------------------------------------------------
class IScrollbarDrawer
//-----------------------------------------------------------------------------
{
public:
virtual void drawScrollbarBackground (CDrawContext* pContext, const CRect& size, CScrollbar::ScrollbarDirection direction, CScrollbar* bar) = 0;
virtual void drawScrollbarScroller (CDrawContext* pContext, const CRect& size, CScrollbar::ScrollbarDirection direction, CScrollbar* bar) = 0;
};
} // VSTGUI
@@ -0,0 +1,167 @@
// 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 "csearchtextedit.h"
#include "../cframe.h"
#include "../cgraphicspath.h"
#include "../cdrawcontext.h"
namespace VSTGUI {
//----------------------------------------------------------------------------------------------------
CSearchTextEdit::CSearchTextEdit (const CRect& size, IControlListener* listener, int32_t tag,
UTF8StringPtr txt, CBitmap* background, const int32_t style)
: CTextEdit (size, listener, tag, nullptr, background, style)
{
setPlaceholderString ("Search");
}
//------------------------------------------------------------------------
void CSearchTextEdit::setClearMarkInset (CPoint inset)
{
if (inset != clearMarkInset)
{
clearMarkInset = inset;
invalid ();
}
}
//------------------------------------------------------------------------
CPoint CSearchTextEdit::getClearMarkInset () const
{
return clearMarkInset;
}
//----------------------------------------------------------------------------------------------------
CRect CSearchTextEdit::getClearMarkRect () const
{
CRect r (getViewSize ());
if (getHoriAlign () == kRightText)
r.right = r.left + getHeight ();
else
r.left = r.right - getHeight ();
r.inset (getClearMarkInset ());
return r;
}
//----------------------------------------------------------------------------------------------------
CMouseEventResult CSearchTextEdit::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (buttons.isLeftButton ())
{
if (!getText ().empty ())
{
if (getClearMarkRect ().pointInside (where))
{
beginEdit ();
setText ("");
valueChanged ();
endEdit ();
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
}
}
return CTextEdit::onMouseDown (where, buttons);
}
//----------------------------------------------------------------------------------------------------
void CSearchTextEdit::drawClearMark (CDrawContext* context) const
{
if (!((platformControl && !platformControl->getText ().empty ()) || !getText ().empty ()))
return;
auto path = owned (context->createGraphicsPath ());
if (path == nullptr)
return;
CRect r = getClearMarkRect ();
CColor color (fontColor);
color.alpha /= 2;
context->setFillColor (color);
context->setDrawMode (kAntiAliasing);
context->drawEllipse (r, kDrawFilled);
double h,s,v;
color.toHSV (h, s, v);
v = 1. - v;
color.fromHSV (h, s, v);
context->setFrameColor (color);
context->setLineWidth (2.);
r.inset (r.getWidth () / (M_PI * 2.) + 1, r.getHeight () / (M_PI * 2.) + 1);
path->beginSubpath (r.getTopLeft ());
path->addLine (r.getBottomRight ());
path->beginSubpath (r.getBottomLeft ());
path->addLine (r.getTopRight ());
context->setDrawMode (kAntiAliasing);
context->drawGraphicsPath (path, CDrawContext::kPathStroked);
}
//----------------------------------------------------------------------------------------------------
void CSearchTextEdit::draw (CDrawContext *pContext)
{
drawBack (pContext);
drawClearMark (pContext);
if (platformControl)
{
setDirty (false);
return;
}
pContext->setDrawMode (kAntiAliasing);
CColor origFontColor (fontColor);
if (getText ().empty ())
{
CColor color (fontColor);
color.alpha /= 2;
setFontColor (color);
drawPlatformText (pContext, getPlaceholderString (), getTextRect ());
}
else
drawPlatformText (pContext, getText (), getTextRect ());
setDirty (false);
setFontColor (origFontColor);
}
//------------------------------------------------------------------------
CRect CSearchTextEdit::getTextRect () const
{
CRect rect = getViewSize ();
CRect cmr = getClearMarkRect ();
if (getHoriAlign () == kRightText)
rect.left = cmr.right;
else
rect.right = cmr.left;
return rect;
}
//------------------------------------------------------------------------
CRect CSearchTextEdit::platformGetSize () const
{
return translateToGlobal (getTextRect ());
}
//------------------------------------------------------------------------
CRect CSearchTextEdit::platformGetVisibleSize () const
{
CRect rect = getTextRect ();
if (getParentView ())
rect = getParentView ()->asViewContainer ()->getVisibleSize (rect);
else if (getFrame ())
rect = getFrame ()->getVisibleSize (rect);
return translateToGlobal (rect);
}
//------------------------------------------------------------------------
void CSearchTextEdit::platformTextDidChange ()
{
invalidRect (getClearMarkRect ());
CTextEdit::platformTextDidChange ();
}
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,39 @@
// 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
#pragma once
#include "ctextedit.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
/** Search text edit field
* @ingroup new_in_4_5
*/
class CSearchTextEdit : public CTextEdit
{
public:
CSearchTextEdit (const CRect& size, IControlListener* listener, int32_t tag,
UTF8StringPtr txt = nullptr, CBitmap* background = nullptr,
const int32_t style = 0);
void setClearMarkInset (CPoint inset);
CPoint getClearMarkInset () const;
void draw (CDrawContext *pContext) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
protected:
void drawClearMark (CDrawContext* context) const;
CRect getClearMarkRect () const;
CRect getTextRect () const;
CRect platformGetSize () const override;
CRect platformGetVisibleSize () const override;
void platformTextDidChange () override;
CPoint clearMarkInset {2., 2.};
};
} // VSTGUI
@@ -0,0 +1,634 @@
// 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 "csegmentbutton.h"
#include "../cdrawcontext.h"
#include "../cframe.h"
#include "../cgraphicspath.h"
#include "../events.h"
#include <algorithm>
namespace VSTGUI {
//-----------------------------------------------------------------------------
CSegmentButton::CSegmentButton (const CRect& size, IControlListener* listener, int32_t tag)
: CControl (size, listener, tag), font (kNormalFont)
{
setWantsFocus (true);
}
//-----------------------------------------------------------------------------
bool CSegmentButton::canAddOneMoreSegment () const
{
return (getSelectionMode () != SelectionMode::kMultiple || segments.size () < 32);
}
//-----------------------------------------------------------------------------
bool CSegmentButton::addSegment (const Segment& segment, uint32_t index)
{
if (!canAddOneMoreSegment ())
return false;
if (index == kPushBack && segments.size () < kPushBack)
segments.emplace_back (segment);
else if (index < segments.size ())
{
auto it = segments.begin ();
std::advance (it, index);
segments.insert (it, segment);
}
updateSegmentSizes ();
return true;
}
//-----------------------------------------------------------------------------
bool CSegmentButton::addSegment (Segment&& segment, uint32_t index)
{
if (!canAddOneMoreSegment ())
return false;
if (index == kPushBack && segments.size () < kPushBack)
segments.emplace_back (std::move (segment));
else if (index < segments.size ())
{
auto it = segments.begin ();
std::advance (it, index);
segments.insert (it, std::move (segment));
}
updateSegmentSizes ();
return true;
}
//-----------------------------------------------------------------------------
void CSegmentButton::removeSegment (uint32_t index)
{
if (index < segments.size ())
{
auto it = segments.begin ();
std::advance (it, index);
segments.erase (it);
}
updateSegmentSizes ();
}
//-----------------------------------------------------------------------------
void CSegmentButton::removeAllSegments ()
{
segments.clear ();
invalid ();
}
//-----------------------------------------------------------------------------
void CSegmentButton::valueChanged ()
{
switch (getSelectionMode ())
{
case SelectionMode::kSingle:
case SelectionMode::kSingleToggle:
{
auto index = static_cast<int64_t> (getSelectedSegment ());
for (auto& segment : segments)
{
bool state = index == 0;
if (state != segment.selected)
{
segment.selected = state;
invalidRect (segment.rect);
}
--index;
}
break;
}
case SelectionMode::kMultiple:
{
auto bitset = static_cast<uint32_t> (value);
size_t index = 0;
for (auto& segment : segments)
{
bool state = (hasBit (bitset, 1 << index));
if (state != segment.selected)
{
segment.selected = state;
invalidRect (segment.rect);
}
++index;
}
break;
}
}
CControl::valueChanged ();
}
//-----------------------------------------------------------------------------
void CSegmentButton::setSelectedSegment (uint32_t index)
{
if (index >= segments.size ())
return;
beginEdit ();
setValueNormalized (static_cast<float> (index) / static_cast<float> (segments.size () - 1));
valueChanged ();
endEdit ();
}
//-----------------------------------------------------------------------------
uint32_t CSegmentButton::getSelectedSegment () const
{
return getSegmentIndex (getValueNormalized ());
}
//-----------------------------------------------------------------------------
void CSegmentButton::setStyle (Style newStyle)
{
if (style != newStyle)
{
style = newStyle;
updateSegmentSizes ();
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setSelectionMode (SelectionMode mode)
{
if (mode != selectionMode)
{
selectionMode = mode;
if (isAttached ())
{
verifySelections ();
invalid ();
}
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setTextTruncateMode (CDrawMethods::TextTruncateMode mode)
{
if (textTruncateMode != mode)
{
textTruncateMode = mode;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setGradient (CGradient* newGradient)
{
if (gradient != newGradient)
{
gradient = newGradient;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setGradientHighlighted (CGradient* newGradient)
{
if (gradientHighlighted != newGradient)
{
gradientHighlighted = newGradient;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setRoundRadius (CCoord newRoundRadius)
{
if (roundRadius != newRoundRadius)
{
roundRadius = newRoundRadius;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setFont (CFontRef newFont)
{
if (font != newFont)
{
font = newFont;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setTextAlignment (CHoriTxtAlign newAlignment)
{
if (textAlignment != newAlignment)
{
textAlignment = newAlignment;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setTextMargin (CCoord newMargin)
{
if (textMargin != newMargin)
{
textMargin = newMargin;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setTextColor (CColor newColor)
{
if (textColor != newColor)
{
textColor = newColor;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setTextColorHighlighted (CColor newColor)
{
if (textColorHighlighted != newColor)
{
textColorHighlighted = newColor;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setFrameColor (CColor newColor)
{
if (frameColor != newColor)
{
frameColor = newColor;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::setFrameWidth (CCoord newWidth)
{
if (frameWidth != newWidth)
{
frameWidth = newWidth;
invalid ();
}
}
//-----------------------------------------------------------------------------
bool CSegmentButton::attached (CView* parent)
{
if (CControl::attached (parent))
{
verifySelections ();
updateSegmentSizes ();
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void CSegmentButton::setViewSize (const CRect& rect, bool invalid)
{
CControl::setViewSize (rect, invalid);
updateSegmentSizes ();
}
//------------------------------------------------------------------------
void CSegmentButton::selectSegment (uint32_t index, bool state)
{
beginEdit ();
auto bitset = static_cast<uint32_t> (value);
setBit (bitset, (1 << index), state);
value = static_cast<float> (bitset);
valueChanged ();
endEdit ();
}
//------------------------------------------------------------------------
bool CSegmentButton::isSegmentSelected (uint32_t index) const
{
return segments[index].selected;
}
//-----------------------------------------------------------------------------
CMouseEventResult CSegmentButton::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (buttons.isLeftButton ())
{
float newValue = 0;
float valueOffset = 1.f / (segments.size () - 1);
for (auto& segment : segments)
{
if (segment.rect.pointInside (where))
{
uint32_t newIndex = getSegmentIndex (newValue);
switch (selectionMode)
{
case SelectionMode::kSingle:
{
uint32_t currentIndex = getSegmentIndex (getValueNormalized ());
if (newIndex != currentIndex)
setSelectedSegment (newIndex);
break;
}
case SelectionMode::kSingleToggle:
{
uint32_t currentIndex = getSegmentIndex (getValueNormalized ());
if (newIndex != currentIndex)
setSelectedSegment (newIndex);
else
{
++currentIndex;
if (getSegments ().size () - 1 < currentIndex)
currentIndex = 0;
setSelectedSegment (currentIndex);
}
break;
}
case SelectionMode::kMultiple:
{
selectSegment (newIndex, !segment.selected);
break;
}
}
break; // out of for loop
}
newValue += valueOffset;
// Last segment can lead to newValue > 1.0
newValue = std::min(newValue, 1.f);
}
}
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
//-----------------------------------------------------------------------------
void CSegmentButton::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type != EventType::KeyDown || event.modifiers.empty () == false ||
event.character != 0)
return;
if (selectionMode != SelectionMode::kMultiple)
{
uint32_t newIndex = getSegmentIndex (getValueNormalized ());
uint32_t oldIndex = newIndex;
switch (event.virt)
{
case VirtualKey::Left:
{
if (style == Style::kHorizontal && newIndex > 0)
newIndex--;
else if (style == Style::kHorizontalInverse && newIndex < segments.size () - 1)
newIndex++;
event.consumed = true;
break;
}
case VirtualKey::Right:
{
if (style == Style::kHorizontal && newIndex < segments.size () - 1)
newIndex++;
else if (style == Style::kHorizontalInverse && newIndex > 0)
newIndex--;
event.consumed = true;
break;
}
case VirtualKey::Up:
{
if (style == Style::kVertical && newIndex > 0)
newIndex--;
else if (style == Style::kVerticalInverse && newIndex < segments.size () - 1)
newIndex++;
event.consumed = true;
break;
}
case VirtualKey::Down:
{
if (style == Style::kVertical && newIndex < segments.size () - 1)
newIndex++;
else if (style == Style::kVerticalInverse && newIndex > 0)
newIndex--;
event.consumed = true;
break;
}
default: return;
}
if (newIndex != oldIndex)
{
setSelectedSegment (newIndex);
}
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::draw (CDrawContext* pContext)
{
CView::draw (pContext);
}
//-----------------------------------------------------------------------------
void CSegmentButton::drawRect (CDrawContext* pContext, const CRect& dirtyRect)
{
if (getOldValue () != getValue ())
verifySelections ();
bool isHorizontal = isHorizontalStyle (style);
bool drawLines = getFrameWidth () != 0. && getFrameColor ().alpha != 0;
auto lineWidth = getFrameWidth ();
if (lineWidth < 0.)
{
lineWidth = pContext->getHairlineSize ();
}
SharedPointer<CGraphicsPath> path;
if (gradient || gradientHighlighted || drawLines)
{
CRect r (getViewSize ());
r.inset (lineWidth / 2., lineWidth / 2.);
path = owned (pContext->createGraphicsPath ());
if (!path)
return;
path->addRoundRect (r, getRoundRadius ());
}
pContext->setDrawMode (kAntiAliasing);
if (drawLines)
{
pContext->setLineStyle (kLineSolid);
pContext->setLineWidth (lineWidth);
pContext->setFrameColor (getFrameColor ());
}
if (gradient)
{
if (isHorizontal)
{
pContext->fillLinearGradient (path, *gradient, getViewSize ().getTopLeft (),
getViewSize ().getBottomLeft ());
}
else
{
pContext->fillLinearGradient (path, *gradient, getViewSize ().getTopLeft (),
getViewSize ().getTopRight ());
}
}
auto lineIndexStart = 1u;
auto lineIndexEnd = segments.size ();
if (isInverseStyle (style))
{
--lineIndexStart;
--lineIndexEnd;
}
for (uint32_t index = 0u, end = static_cast<uint32_t> (segments.size ()); index < end; ++index)
{
const auto& segment = segments[index];
if (!dirtyRect.rectOverlap (segment.rect))
continue;
drawClipped (pContext, segment.rect, [&] () {
if (segment.selected && gradientHighlighted)
{
if (isHorizontal)
{
pContext->fillLinearGradient (path, *gradientHighlighted,
segment.rect.getTopLeft (),
segment.rect.getBottomLeft ());
}
else
{
pContext->fillLinearGradient (path, *gradientHighlighted,
segment.rect.getTopLeft (),
segment.rect.getTopRight ());
}
}
if (segment.selected && segment.backgroundHighlighted)
{
segment.backgroundHighlighted->draw (pContext, segment.rect);
}
else if (segment.background)
{
segment.background->draw (pContext, segment.rect);
}
CDrawMethods::drawIconAndText (
pContext, segment.selected ? segment.iconHighlighted : segment.icon,
segment.iconPosition, textAlignment, textMargin, segment.rect, segment.name, font,
segment.selected ? textColorHighlighted : textColor, textTruncateMode);
});
if (drawLines && index >= lineIndexStart && index < lineIndexEnd)
{
path->beginSubpath (segment.rect.getTopLeft ());
path->addLine (isHorizontal ? segment.rect.getBottomLeft () :
segment.rect.getTopRight ());
}
}
if (drawLines)
pContext->drawGraphicsPath (path, CDrawContext::kPathStroked);
setDirty (false);
}
//-----------------------------------------------------------------------------
uint32_t CSegmentButton::getSegmentIndex (float value) const
{
if (value < 0.f || value > 1.f)
return kPushBack;
return std::min<uint32_t> (static_cast<uint32_t> (segments.size () - 1),
static_cast<uint32_t> (value * (segments.size ())));
}
//-----------------------------------------------------------------------------
void CSegmentButton::updateSegmentSizes ()
{
if (isAttached () && !segments.empty ())
{
switch (style)
{
case Style::kHorizontal:
{
CCoord width = getWidth () / segments.size ();
CRect r (getViewSize ());
r.setWidth (width);
for (auto& segment : segments)
{
segment.rect = r;
r.offset (width, 0);
}
break;
}
case Style::kHorizontalInverse:
{
CCoord width = getWidth () / segments.size ();
CRect r (getViewSize ());
r.setWidth (width);
for (auto it = segments.rbegin(); it != segments.rend(); ++it)
{
(*it).rect = r;
r.offset (width, 0);
}
break;
}
case Style::kVertical:
{
CCoord height = getHeight () / segments.size ();
CRect r (getViewSize ());
r.setHeight (height);
for (auto& segment : segments)
{
segment.rect = r;
r.offset (0, height);
}
break;
}
case Style::kVerticalInverse:
{
CCoord height = getHeight () / segments.size ();
CRect r (getViewSize ());
r.setHeight (height);
for (auto it = segments.rbegin(); it != segments.rend(); ++it)
{
(*it).rect = r;
r.offset (0, height);
}
break;
}
}
}
}
//-----------------------------------------------------------------------------
void CSegmentButton::verifySelections ()
{
if (selectionMode == SelectionMode::kMultiple)
{
auto bitset = static_cast<uint32_t> (value);
for (auto index = 0u; index < segments.size (); ++index)
{
segments[index].selected = (bitset & (1 << index)) != 0;
}
}
else
{
auto selectedIndex = getSelectedSegment ();
if (selectedIndex > segments.size ())
selectedIndex = 0;
for (auto& segment : segments)
segment.selected = false;
segments[selectedIndex].selected = true;
}
}
//-----------------------------------------------------------------------------
bool CSegmentButton::drawFocusOnTop ()
{
return false;
}
//-----------------------------------------------------------------------------
bool CSegmentButton::getFocusPath (CGraphicsPath& outPath)
{
auto lineWidth = getFrameWidth ();
if (lineWidth < 0.)
lineWidth = 1.;
CRect r (getViewSize ());
r.inset (lineWidth / 2., lineWidth / 2.);
outPath.addRoundRect (r, getRoundRadius ());
CCoord focusWidth = getFrame ()->getFocusWidth ();
r.extend (focusWidth, focusWidth);
outPath.addRoundRect (r, getRoundRadius ());
return true;
}
} // VSTGUI
@@ -0,0 +1,177 @@
// 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
#pragma once
#include "ccontrol.h"
#include "../cdrawmethods.h"
#include "../cbitmap.h"
#include "../cgradient.h"
#include "../cstring.h"
#include "../ccolor.h"
#include <vector>
#include <limits>
namespace VSTGUI {
//-----------------------------------------------------------------------------
/// @brief Control which draws a segmented button
/// @ingroup new_in_4_3
//-----------------------------------------------------------------------------
class CSegmentButton : public CControl
{
public:
enum class Style {
/** horizontally layouted segments */
kHorizontal,
/** vertically layouted segments */
kVertical,
/** horizontally inverse layouted segments */
kHorizontalInverse,
/** vertically inverse layouted segments */
kVerticalInverse,
};
enum class SelectionMode
{
/** a single segment is selected at any time */
kSingle,
/** a single segment is selected at any time, when a segment is clicked which is already
selected, the next segment is selected */
kSingleToggle,
/** multiple segments may be selected */
kMultiple,
};
struct Segment {
mutable UTF8String name;
mutable SharedPointer<CBitmap> icon;
mutable SharedPointer<CBitmap> iconHighlighted;
mutable SharedPointer<CBitmap> background;
mutable SharedPointer<CBitmap> backgroundHighlighted;
mutable CDrawMethods::IconPosition iconPosition;
CRect rect;
bool selected {false};
};
using Segments = std::vector<Segment>;
static constexpr uint32_t kPushBack = (std::numeric_limits<uint32_t>::max) ();
CSegmentButton (const CRect& size, IControlListener* listener = nullptr, int32_t tag = -1);
//-----------------------------------------------------------------------------
/// @name Segment Methods
//-----------------------------------------------------------------------------
//@{
bool addSegment (const Segment& segment, uint32_t index = kPushBack);
bool addSegment (Segment&& segment, uint32_t index = kPushBack);
void removeSegment (uint32_t index);
void removeAllSegments ();
const Segments& getSegments () const { return segments; }
/** set the selected segment in single selection mode */
void setSelectedSegment (uint32_t index);
/** get the selected segment in single selection mode */
uint32_t getSelectedSegment () const;
/** set selection state for a segment in multiple selection mode */
void selectSegment (uint32_t index, bool state);
/** get selection state for a segment in multiple selection mode */
bool isSegmentSelected (uint32_t index) const;
//@}
//-----------------------------------------------------------------------------
/// @name CSegmentButton Style Methods
//-----------------------------------------------------------------------------
//@{
void setStyle (Style newStyle);
Style getStyle () const { return style; }
void setSelectionMode (SelectionMode mode);
SelectionMode getSelectionMode () const { return selectionMode; }
void setTextTruncateMode (CDrawMethods::TextTruncateMode mode);
CDrawMethods::TextTruncateMode getTextTruncateMode () const { return textTruncateMode; }
void setGradient (CGradient* newGradient);
CGradient* getGradient () const { return gradient; }
void setGradientHighlighted (CGradient* newGradient);
CGradient* getGradientHighlighted () const { return gradientHighlighted; }
void setRoundRadius (CCoord newRoundRadius);
CCoord getRoundRadius () const { return roundRadius; }
void setFont (CFontRef font);
CFontRef getFont () const { return font; }
void setTextAlignment (CHoriTxtAlign alignment);
CHoriTxtAlign getTextAlignment () const { return textAlignment; }
void setTextMargin (CCoord newMargin);
CCoord getTextMargin () const { return textMargin; }
void setTextColor (CColor newColor);
CColor getTextColor () const { return textColor; }
void setTextColorHighlighted (CColor newColor);
CColor getTextColorHighlighted () const { return textColorHighlighted; }
void setFrameColor (CColor newColor);
CColor getFrameColor () const { return frameColor; }
void setFrameWidth (CCoord newWidth);
CCoord getFrameWidth () const { return frameWidth; }
//@}
// overrides
bool attached (CView *parent) override;
void setViewSize (const CRect& rect, bool invalid = true) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
void onKeyboardEvent (KeyboardEvent& event) override;
void draw (CDrawContext* pContext) override;
void drawRect (CDrawContext* pContext, const CRect& dirtyRect) override;
bool drawFocusOnTop () override;
bool getFocusPath (CGraphicsPath& outPath) override;
void valueChanged () override;
static bool isHorizontalStyle (Style style)
{
return style == Style::kHorizontal || style == Style::kHorizontalInverse;
}
static bool isVerticalStyle (Style style)
{
return style == Style::kVertical || style == Style::kVerticalInverse;
}
static bool isInverseStyle (Style style)
{
return style == Style::kHorizontalInverse || style == Style::kVerticalInverse;
}
CLASS_METHODS (CSegmentButton, CControl)
private:
bool canAddOneMoreSegment () const;
void updateSegmentSizes ();
void verifySelections ();
uint32_t getSegmentIndex (float value) const;
Segments segments;
SharedPointer<CGradient> gradient;
SharedPointer<CGradient> gradientHighlighted;
SharedPointer<CFontDesc> font;
CColor textColor {kBlackCColor};
CColor textColorHighlighted {kWhiteCColor};
CColor frameColor {kBlackCColor};
CHoriTxtAlign textAlignment {kCenterText};
CCoord textMargin {0.};
CCoord roundRadius {5.};
CCoord frameWidth {1.};
Style style {Style::kHorizontal};
SelectionMode selectionMode {SelectionMode::kSingle};
CDrawMethods::TextTruncateMode textTruncateMode {CDrawMethods::kTextTruncateNone};
};
} // VSTGUI
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,197 @@
// 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
#pragma once
#include "../ccolor.h"
#include "../enumbitset.h"
#include "ccontrol.h"
namespace VSTGUI {
//------------------------------------------------------------------------
class CSliderBase : public CControl, protected CMouseWheelEditingSupport
{
public:
enum Style : int32_t
{
kHorizontal,
kVertical,
kLeft,
kRight,
kTop,
kBottom,
};
using Styles = EnumBitset<Style>;
CSliderBase (const CRect& size, IControlListener* listener, int32_t tag);
CSliderBase (const CSliderBase& slider);
void setOffsetHandle (const CPoint& val);
CPoint getOffsetHandle () const;
void setStyle (Styles style);
Styles getStyle () const;
bool isStyleHorizontal () const;
bool isStyleRight () const;
bool isStyleBottom () const;
bool isInverseStyle () const;
void setZoomFactor (float val);
float getZoomFactor () const;
void setSliderMode (CSliderMode mode);
CSliderMode getSliderMode () const;
CSliderMode getEffectiveSliderMode () const;
static void setGlobalMode (CSliderMode mode);
static CSliderMode getGlobalMode ();
// overrides
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
void onMouseWheelEvent (MouseWheelEvent& event) override;
void onKeyboardEvent (KeyboardEvent& event) override;
void setViewSize (const CRect& rect, bool invalid) override;
static bool kAlwaysUseZoomFactor;
protected:
~CSliderBase () noexcept;
CRect calculateHandleRect (float normValue) const;
// for sub-classes to access private variables:
void setHandleSizePrivate (CCoord width, CCoord height);
CPoint getHandleSizePrivate () const;
CPoint getControlSizePrivate () const;
void setHandleRangePrivate (CCoord range);
void setHandleMinPosPrivate (CCoord pos);
CCoord getHandleMinPosPrivate () const;
private:
void updateInternalHandleValues ();
float calculateDelta (const CPoint& where, CRect* handleRect = nullptr) const;
void doRamping ();
struct Impl;
std::unique_ptr<Impl> impl;
};
//------------------------------------------------------------------------
// CSlider Declaration
//! @brief a slider control
/// @ingroup controls
//------------------------------------------------------------------------
class CSlider : public CSliderBase
{
private:
public:
CSlider (const CRect& size, IControlListener* listener, int32_t tag, int32_t iMinPos,
int32_t iMaxPos, CBitmap* handle, CBitmap* background,
const CPoint& offset = CPoint (0, 0), Styles style = {{kLeft, kHorizontal}});
CSlider (const CRect& rect, IControlListener* listener, int32_t tag, const CPoint& offsetHandle,
int32_t rangeHandle, CBitmap* handle, CBitmap* background,
const CPoint& offset = CPoint (0, 0), Styles style = {{kLeft, kHorizontal}});
CSlider (const CSlider& slider);
//------------------------------------------------------------------------
/// @name CSlider Methods
//------------------------------------------------------------------------
//@{
VSTGUI_DEPRECATED (
/** \deprecated use setBackgroundOffset */
virtual void setOffset (const CPoint& val);)
VSTGUI_DEPRECATED (
/** \deprecated use getBackgroundOffset*/
virtual CPoint getOffset () const;)
/** set background draw offset */
void setBackgroundOffset (const CPoint& offset);
/** get background draw offset */
CPoint getBackgroundOffset () const;
virtual void setHandle (CBitmap* pHandle);
virtual CBitmap* getHandle () const;
//@}
//------------------------------------------------------------------------
/// @name Draw Style Methods
//------------------------------------------------------------------------
//@{
enum DrawStyle
{
kDrawFrame = 1 << 0,
kDrawBack = 1 << 1,
kDrawValue = 1 << 2,
kDrawValueFromCenter = 1 << 3,
kDrawInverted = 1 << 4
};
virtual void setDrawStyle (int32_t style);
virtual void setFrameWidth (CCoord width);
virtual void setFrameColor (CColor color);
virtual void setBackColor (CColor color);
virtual void setValueColor (CColor color);
int32_t getDrawStyle () const;
CCoord getFrameWidth () const;
CColor getFrameColor () const;
CColor getBackColor () const;
CColor getValueColor () const;
//@}
// overrides
void draw (CDrawContext*) override;
bool sizeToFit () override;
CLASS_METHODS (CSlider, CControl)
protected:
~CSlider () noexcept override;
struct Impl;
std::unique_ptr<Impl> impl;
};
//------------------------------------------------------------------------
// CVerticalSlider Declaration
//! @brief a vertical slider control
/// @ingroup controls
//------------------------------------------------------------------------
class CVerticalSlider : public CSlider
{
public:
CVerticalSlider (const CRect& size, IControlListener* listener, int32_t tag, int32_t iMinPos,
int32_t iMaxPos, CBitmap* handle, CBitmap* background,
const CPoint& offset = CPoint (0, 0), Styles style = kBottom);
CVerticalSlider (const CRect& rect, IControlListener* listener, int32_t tag,
const CPoint& offsetHandle, int32_t rangeHandle, CBitmap* handle,
CBitmap* background, const CPoint& offset = CPoint (0, 0),
Styles style = kBottom);
CVerticalSlider (const CVerticalSlider& slider) = default;
};
//------------------------------------------------------------------------
// CHorizontalSlider Declaration
//! @brief a horizontal slider control
/// @ingroup controls
//------------------------------------------------------------------------
class CHorizontalSlider : public CSlider
{
public:
CHorizontalSlider (const CRect& size, IControlListener* listener, int32_t tag, int32_t iMinPos,
int32_t iMaxPos, CBitmap* handle, CBitmap* background,
const CPoint& offset = CPoint (0, 0), Styles style = kRight);
CHorizontalSlider (const CRect& rect, IControlListener* listener, int32_t tag,
const CPoint& offsetHandle, int32_t rangeHandle, CBitmap* handle,
CBitmap* background, const CPoint& offset = CPoint (0, 0),
Styles style = kRight);
CHorizontalSlider (const CHorizontalSlider& slider) = default;
};
} // VSTGUI
@@ -0,0 +1,130 @@
// 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 "cspecialdigit.h"
#include "../cdrawcontext.h"
#include "../cbitmap.h"
#include <cmath>
namespace VSTGUI {
//------------------------------------------------------------------------
// CSpecialDigit
//------------------------------------------------------------------------
/*! @class CSpecialDigit
Can be used to display a counter with maximum 7 digits.
All digit have the same size and are stacked in height in the bitmap.
*/
//------------------------------------------------------------------------
/**
* CSpecialDigit constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param dwPos actual value
* @param iNumbers amount of numbers (max 7)
* @param xpos array of all X positions, can be NULL
* @param ypos array of all Y positions, can be NULL
* @param width width of one number in the bitmap
* @param height height of one number in the bitmap
* @param background bitmap
*/
//------------------------------------------------------------------------
CSpecialDigit::CSpecialDigit (const CRect& size, IControlListener* listener, int32_t tag, int32_t dwPos, int32_t inNumbers, int32_t* xpos, int32_t* ypos, int32_t width, int32_t height, CBitmap* background)
: CControl (size, listener, tag, background)
, iNumbers (inNumbers)
, width (width)
, height (height)
{
setValue ((float)dwPos); // actual value
if (iNumbers > 7)
iNumbers = 7;
if (xpos == nullptr)
{
// automatically init xpos/ypos if not provided by caller
const int32_t numw = (const int32_t)background->getWidth();
int32_t x = (int32_t)size.left;
for (int32_t i = 0; i < inNumbers; i++)
{
this->xpos[i] = x;
this->ypos[i] = (int32_t)size.top;
x += numw;
}
}
else if (xpos && ypos)
{
// store coordinates of x/y pos of each digit
for (int32_t i = 0; i < inNumbers; i++)
{
this->xpos[i] = xpos[i];
this->ypos[i] = ypos[i];
}
}
setMax ((float)pow (10.f, (float)inNumbers) - 1.0f);
setMin (0.0f);
}
//------------------------------------------------------------------------
CSpecialDigit::CSpecialDigit (const CSpecialDigit& v)
: CControl (v)
, iNumbers (v.iNumbers)
, width (v.width)
, height (v.height)
{
for (int32_t i = 0; i < 7; i++)
{
xpos[i] = v.xpos[i];
ypos[i] = v.ypos[i];
}
}
//------------------------------------------------------------------------
void CSpecialDigit::draw (CDrawContext *pContext)
{
CPoint where;
CRect rectDest;
int32_t i, j;
int32_t one_digit[16] = {};
int32_t dwValue = static_cast<int32_t> (getValue ());
int32_t intMax = static_cast<int32_t> (getMax ());
if (dwValue > intMax)
dwValue = intMax;
else if (dwValue < static_cast<int32_t> (getMin ()))
dwValue = static_cast<int32_t> (getMin ());
for (i = 0, j = (intMax + 1) / 10; i < iNumbers; i++, j /= 10)
{
one_digit[i] = dwValue / j;
dwValue -= (one_digit[i] * j);
}
where.x = 0;
for (i = 0; i < iNumbers; i++)
{
j = one_digit[i];
if (j > 9)
j = 9;
rectDest.left = (CCoord)xpos[i];
rectDest.top = (CCoord)ypos[i];
rectDest.right = rectDest.left + width;
rectDest.bottom = rectDest.top + height;
// where = src from bitmap
where.y = (CCoord)j * height;
if (getDrawBackground ())
{
getDrawBackground ()->draw (pContext, rectDest, where);
}
}
setDirty (false);
}
} // VSTGUI
@@ -0,0 +1,34 @@
// 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
#pragma once
#include "ccontrol.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CSpecialDigit Declaration
//! @brief special display with custom numbers (0...9)
/// @ingroup views
//-----------------------------------------------------------------------------
class CSpecialDigit : public CControl
{
public:
CSpecialDigit (const CRect& size, IControlListener* listener, int32_t tag, int32_t dwPos, int32_t iNumbers, int32_t* xpos, int32_t* ypos, int32_t width, int32_t height, CBitmap* background);
CSpecialDigit (const CSpecialDigit& digit);
void draw (CDrawContext*) override;
CLASS_METHODS(CSpecialDigit, CControl)
protected:
~CSpecialDigit () noexcept override = default;
int32_t iNumbers;
int32_t xpos[7];
int32_t ypos[7];
int32_t width;
int32_t height;
};
} // VSTGUI
@@ -0,0 +1,334 @@
// 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 "csplashscreen.h"
#include "../cdrawcontext.h"
#include "../cbitmap.h"
#include "../cframe.h"
#include "../animation/animations.h"
#include "../animation/timingfunctions.h"
#include "../events.h"
namespace VSTGUI {
/// @cond ignore
//------------------------------------------------------------------------
class CDefaultSplashScreenView : public CControl
{
public:
CDefaultSplashScreenView (const CRect& size, IControlListener* listener, CBitmap* bitmap,
const CPoint& offset)
: CControl (size, listener), offset (offset)
{
setBackground (bitmap);
}
void draw (CDrawContext *pContext) override
{
if (getDrawBackground ())
getDrawBackground ()->draw (pContext, getViewSize (), offset);
setDirty (false);
}
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override
{
if (buttons.isLeftButton ())
{
valueChanged ();
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
return kMouseEventNotHandled;
}
CLASS_METHODS(CDefaultSplashScreenView, CControl)
protected:
CPoint offset;
};
/// @endcond
//------------------------------------------------------------------------
// CSplashScreen
//------------------------------------------------------------------------
/*! @class CSplashScreen
One click on its activated region and its bitmap or view is displayed, in this state the other controls can not be used,
and another click on the displayed area will leave the modal mode.
*/
//------------------------------------------------------------------------
/**
* CSplashScreen constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background the bitmap
* @param toDisplay the region where to display the bitmap
* @param offset offset of background bitmap
*/
//------------------------------------------------------------------------
CSplashScreen::CSplashScreen (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background, const CRect& toDisplay, const CPoint& offset)
: CControl (size, listener, tag, background), toDisplay (toDisplay), offset (offset)
{
modalView = new CDefaultSplashScreenView (toDisplay, this, background, offset);
}
//------------------------------------------------------------------------
/**
* CSplashScreen constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param splashView the view to show
*/
//------------------------------------------------------------------------
CSplashScreen::CSplashScreen (const CRect& size, IControlListener* listener, int32_t tag, CView* splashView)
: CControl (size, listener, tag)
, modalView (splashView)
{
}
//------------------------------------------------------------------------
CSplashScreen::CSplashScreen (const CSplashScreen& v)
: CControl (v)
, toDisplay (v.toDisplay)
, keepSize (v.keepSize)
, offset (v.offset)
{
modalView = static_cast<CView*> (v.modalView->newCopy ());
}
//------------------------------------------------------------------------
CSplashScreen::~CSplashScreen () noexcept
{
if (modalView)
modalView->forget ();
}
//------------------------------------------------------------------------
void CSplashScreen::draw (CDrawContext *pContext)
{
setDirty (false);
}
//------------------------------------------------------------------------
bool CSplashScreen::hitTest (const CPoint& where, const Event& event)
{
bool result = CView::hitTest (where, event);
if (result)
{
if (auto mouseEvent = asMouseEvent (event))
{
if (!mouseEvent->buttonState.isLeft ())
return false;
}
}
return result;
}
//------------------------------------------------------------------------
CMouseEventResult CSplashScreen::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (buttons & kLButton)
{
value = (value == getMax ()) ? getMin () : getMax ();
if (value == getMax () && !modalViewSessionID && modalView)
{
if (auto frame = getFrame ())
{
if (modalView)
{
if ((modalViewSessionID = frame->beginModalViewSession (modalView)))
{
modalView->remember ();
CControl::valueChanged ();
}
}
}
}
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
void CSplashScreen::valueChanged (CControl *pControl)
{
if (pControl == modalView)
{
unSplash ();
CControl::valueChanged ();
}
}
//------------------------------------------------------------------------
void CSplashScreen::unSplash ()
{
value = getMin ();
if (auto frame = getFrame ())
{
if (modalViewSessionID)
{
if (modalView)
modalView->invalid ();
frame->endModalViewSession (*modalViewSessionID);
modalViewSessionID = {};
}
}
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
CAnimationSplashScreen::CAnimationSplashScreen (const CRect& size, int32_t tag, CBitmap* background, CBitmap* splashBitmap)
: CSplashScreen (size, nullptr, tag, splashBitmap, CRect (0, 0, 0, 0))
{
CView::setBackground (background);
}
//------------------------------------------------------------------------
void CAnimationSplashScreen::setSplashBitmap (CBitmap* bitmap)
{
if (modalView)
{
modalView->setBackground (bitmap);
}
}
//------------------------------------------------------------------------
CBitmap* CAnimationSplashScreen::getSplashBitmap () const
{
if (modalView)
return modalView->getBackground ();
return nullptr;
}
//------------------------------------------------------------------------
void CAnimationSplashScreen::setSplashRect (const CRect& splashRect)
{
if (modalView)
{
modalView->setViewSize (splashRect);
modalView->setMouseableArea (splashRect);
}
}
//------------------------------------------------------------------------
const CRect& CAnimationSplashScreen::getSplashRect () const
{
if (modalView)
return modalView->getViewSize ();
return getViewSize ();
}
//------------------------------------------------------------------------
CMouseEventResult CAnimationSplashScreen::onMouseDown (CPoint& where, const CButtonState& buttons)
{
CMouseEventResult result = CSplashScreen::onMouseDown (where, buttons);
if (modalView && value == getMax ())
{
createAnimation (animationIndex, animationTime, modalView, false);
}
return result;
}
//------------------------------------------------------------------------
void CAnimationSplashScreen::unSplash ()
{
value = getMin ();
if (auto frame = getFrame ())
{
if (frame->getModalView () == modalView)
{
if (!createAnimation (animationIndex, animationTime, modalView, true))
{
if (modalView)
modalView->invalid ();
if (modalViewSessionID)
{
frame->endModalViewSession (*modalViewSessionID);
modalViewSessionID = {};
}
setMouseEnabled (true);
}
}
}
}
//------------------------------------------------------------------------
void CAnimationSplashScreen::draw (CDrawContext *pContext)
{
CView::draw (pContext);
setDirty (false);
}
//------------------------------------------------------------------------
bool CAnimationSplashScreen::sizeToFit ()
{
if (modalView && modalView->getBackground ())
{
CRect r = modalView->getViewSize ();
r.setWidth (modalView->getBackground ()->getWidth ());
r.setHeight (modalView->getBackground ()->getHeight ());
if (getFrame ())
{
r.centerInside (getFrame ()->getViewSize ());
}
modalView->setViewSize (r);
modalView->setMouseableArea (r);
}
if (getBackground ())
{
CRect r = getViewSize ();
r.setWidth (getBackground ()->getWidth ());
r.setHeight (getBackground ()->getHeight ());
setViewSize (r);
setMouseableArea (r);
}
return true;
}
//------------------------------------------------------------------------
bool CAnimationSplashScreen::createAnimation (uint32_t animIndex, uint32_t animTime,
CView* splashView, bool removeViewAnimation)
{
if (!isAttached ())
return false;
switch (animIndex)
{
case 0:
{
if (removeViewAnimation)
{
splashView->setMouseEnabled (false);
splashView->addAnimation (
"AnimationSplashScreenAnimation", new Animation::AlphaValueAnimation (0.f),
new Animation::PowerTimingFunction (animTime, 2),
[this] (CView*, const IdStringPtr, Animation::IAnimationTarget*) {
if (modalView)
{
modalView->invalid ();
modalView->setMouseEnabled (true);
}
if (modalViewSessionID)
{
if (auto frame = getFrame ())
frame->endModalViewSession (*modalViewSessionID);
modalViewSessionID = {};
}
setMouseEnabled (true);
});
}
else
{
setMouseEnabled (false);
splashView->setAlphaValue (0.f);
splashView->addAnimation ("AnimationSplashScreenAnimation", new Animation::AlphaValueAnimation (1.f), new Animation::PowerTimingFunction (animTime, 2));
}
return true;
}
}
return false;
}
} // VSTGUI
@@ -0,0 +1,99 @@
// 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
#pragma once
#include "ccontrol.h"
#include "icontrollistener.h"
#include "../optional.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CSplashScreen Declaration
//!
/// @ingroup views
//-----------------------------------------------------------------------------
class CSplashScreen : public CControl, public IControlListener
{
public:
CSplashScreen (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, const CRect& toDisplay, const CPoint& offset = CPoint (0, 0));
CSplashScreen (const CRect& size, IControlListener* listener, int32_t tag, CView* splashView);
CSplashScreen (const CSplashScreen& splashScreen);
void draw (CDrawContext*) override;
bool hitTest (const CPoint& where, const Event& event) override;
//-----------------------------------------------------------------------------
/// @name CSplashScreen Methods
//-----------------------------------------------------------------------------
//@{
virtual void unSplash ();
/** set the area in which the splash will be displayed */
virtual void setDisplayArea (const CRect& rect) { toDisplay = rect; }
/** get the area in which the splash will be displayed */
virtual CRect& getDisplayArea (CRect& rect) const { rect = toDisplay; return rect; }
//@}
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CLASS_METHODS(CSplashScreen, CControl)
protected:
~CSplashScreen () noexcept override;
using CControl::valueChanged;
void valueChanged (CControl *pControl) override;
CRect toDisplay;
CRect keepSize;
CPoint offset;
CView* modalView{nullptr};
Optional<ModalViewSessionID> modalViewSessionID;
};
//-----------------------------------------------------------------------------
// CAnimationSplashScreen Declaration
/// @brief a splash screen which animates the opening and closing of the splash bitmap
/// @ingroup views
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class CAnimationSplashScreen : public CSplashScreen
{
public:
CAnimationSplashScreen (const CRect& size, int32_t tag, CBitmap* background, CBitmap* splashBitmap);
CAnimationSplashScreen (const CAnimationSplashScreen& splashScreen) = default;
//-----------------------------------------------------------------------------
/// @name CAnimationSplashScreen Methods
//-----------------------------------------------------------------------------
//@{
virtual void setSplashBitmap (CBitmap* bitmap);
CBitmap* getSplashBitmap () const;
virtual void setSplashRect (const CRect& splashRect);
const CRect& getSplashRect () const;
virtual void setAnimationIndex (uint32_t index) { animationIndex = index; }
uint32_t getAnimationIndex () const { return animationIndex; }
virtual void setAnimationTime (uint32_t time) { animationTime = time; }
uint32_t getAnimationTime () const { return animationTime; }
/** create the animation. subclasses can override this to add special animations */
virtual bool createAnimation (uint32_t animationIndex, uint32_t animationTime, CView* splashView, bool removeViewAnimation);
//@}
void unSplash () override;
void draw (CDrawContext*) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
bool sizeToFit () override;
protected:
~CAnimationSplashScreen () noexcept override = default;
uint32_t animationIndex{0};
uint32_t animationTime{500};
};
} // VSTGUI
@@ -0,0 +1,222 @@
// 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 "cstringlist.h"
#include "../cdrawcontext.h"
#include "../ccolor.h"
#include "../cfont.h"
#include "../platform/platformfactory.h"
#include "../platform/iplatformstring.h"
//------------------------------------------------------------------------
namespace VSTGUI {
//------------------------------------------------------------------------
struct StringListControlDrawer::Impl
{
Func func;
SharedPointer<CFontDesc> font {kNormalFont};
CColor fontColor {kBlackCColor};
CColor fontColorSelected {kWhiteCColor};
CColor backColor {kWhiteCColor};
CColor backColorSelected {kBlueCColor};
CColor hoverColor {MakeCColor (0, 0, 0, 100)};
CColor lineColor {kBlackCColor};
CCoord lineWidth {1.};
CCoord textInset {5.};
CHoriTxtAlign textAlign {kLeftText};
};
//------------------------------------------------------------------------
StringListControlDrawer::StringListControlDrawer ()
{
impl = std::unique_ptr<Impl> (new Impl);
}
//------------------------------------------------------------------------
StringListControlDrawer::~StringListControlDrawer () noexcept = default;
//------------------------------------------------------------------------
void StringListControlDrawer::setStringProvider (Func&& getStringFunc)
{
impl->func = std::move (getStringFunc);
}
//------------------------------------------------------------------------
void StringListControlDrawer::setStringProvider (const Func& getStringFunc)
{
impl->func = getStringFunc;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setFont (CFontRef f)
{
impl->font = f;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setFontColor (CColor color)
{
impl->fontColor = color;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setSelectedFontColor (CColor color)
{
impl->fontColorSelected = color;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setBackColor (CColor color)
{
impl->backColor = color;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setSelectedBackColor (CColor color)
{
impl->backColorSelected = color;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setHoverColor (CColor color)
{
impl->hoverColor = color;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setLineColor (CColor color)
{
impl->lineColor = color;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setLineWidth (CCoord width)
{
impl->lineWidth = width;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setTextInset (CCoord inset)
{
impl->textInset = inset;
}
//------------------------------------------------------------------------
void StringListControlDrawer::setTextAlign (CHoriTxtAlign align)
{
impl->textAlign = align;
}
//------------------------------------------------------------------------
CFontRef StringListControlDrawer::getFont () const
{
return impl->font;
}
//------------------------------------------------------------------------
CColor StringListControlDrawer::getFontColor () const
{
return impl->fontColor;
}
//------------------------------------------------------------------------
CColor StringListControlDrawer::getSelectedFontColor () const
{
return impl->fontColorSelected;
}
//------------------------------------------------------------------------
CColor StringListControlDrawer::getBackColor () const
{
return impl->backColor;
}
//------------------------------------------------------------------------
CColor StringListControlDrawer::getSelectedBackColor () const
{
return impl->backColorSelected;
}
//------------------------------------------------------------------------
CColor StringListControlDrawer::getHoverColor () const
{
return impl->hoverColor;
}
//------------------------------------------------------------------------
CColor StringListControlDrawer::getLineColor () const
{
return impl->lineColor;
}
//------------------------------------------------------------------------
CCoord StringListControlDrawer::getLineWidth () const
{
return impl->lineWidth;
}
//------------------------------------------------------------------------
CCoord StringListControlDrawer::getTextInset () const
{
return impl->textInset;
}
//------------------------------------------------------------------------
CHoriTxtAlign StringListControlDrawer::getTextAlign () const
{
return impl->textAlign;
}
//------------------------------------------------------------------------
void StringListControlDrawer::drawBackground (CDrawContext* context, CRect size)
{
context->setFillColor (impl->backColor);
context->drawRect (size, kDrawFilled);
}
//------------------------------------------------------------------------
void StringListControlDrawer::drawRow (CDrawContext* context, CRect size, Row row)
{
context->setDrawMode (kAntiAliasing);
if (row.isHovered ())
{
context->setFillColor (impl->hoverColor);
context->drawRect (size, kDrawFilled);
}
if (row.isSelected ())
{
context->setFillColor (impl->backColorSelected);
context->drawRect (size, kDrawFilled);
}
auto lw = impl->lineWidth < 0. ? context->getHairlineSize () : impl->lineWidth;
size.bottom -= lw * 0.5;
if (!(row.isLastRow ()) && lw != 0.)
{
context->setDrawMode (kAntiAliasing | kNonIntegralMode);
context->setFrameColor (impl->lineColor);
context->setLineWidth (lw);
context->drawLine (size.getBottomLeft (), size.getBottomRight ());
}
if (auto string = getString (row))
{
size.inset (impl->textInset, 0);
context->setFontColor (row.isSelected () ? impl->fontColorSelected : impl->fontColor);
context->setFont (impl->font);
context->drawString (string, size, impl->textAlign);
}
}
//------------------------------------------------------------------------
PlatformStringPtr StringListControlDrawer::getString (int32_t row) const
{
return impl->func ? impl->func (row) : getPlatformFactory ().createString (toString (row));
}
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,66 @@
// 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
#pragma once
#include "clistcontrol.h"
#include "../cdrawdefs.h"
//------------------------------------------------------------------------
namespace VSTGUI {
//------------------------------------------------------------------------
/** A specialized list control drawer to draw strings
*
* You set an instance of this class as the drawer in a CListControl instance and it draws the
* strings you setup via the provider function.
*
* @ingroup new_in_4_9
*/
//------------------------------------------------------------------------
class StringListControlDrawer : public IListControlDrawer, public NonAtomicReferenceCounted
{
public:
using Func = std::function<PlatformStringPtr (int32_t row)>;
StringListControlDrawer ();
~StringListControlDrawer () noexcept override;
void setStringProvider (Func&& getStringFunc);
void setStringProvider (const Func& getStringFunc);
void setFont (CFontRef f);
void setFontColor (CColor color);
void setSelectedFontColor (CColor color);
void setBackColor (CColor color);
void setSelectedBackColor (CColor color);
void setHoverColor (CColor color);
void setLineColor (CColor color);
void setLineWidth (CCoord width);
void setTextInset (CCoord inset);
void setTextAlign (CHoriTxtAlign align);
CFontRef getFont () const;
CColor getFontColor () const;
CColor getSelectedFontColor () const;
CColor getBackColor () const;
CColor getSelectedBackColor () const;
CColor getHoverColor () const;
CColor getLineColor () const;
CCoord getLineWidth () const;
CCoord getTextInset () const;
CHoriTxtAlign getTextAlign () const;
void drawBackground (CDrawContext* context, CRect size) override;
void drawRow (CDrawContext* context, CRect size, Row row) override;
private:
PlatformStringPtr getString (int32_t row) const;
struct Impl;
std::unique_ptr<Impl> impl;
};
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,770 @@
// 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 "cswitch.h"
#include "../cdrawcontext.h"
#include "../cbitmap.h"
#include "../cvstguitimer.h"
#include "../events.h"
#include "../algorithm.h"
namespace VSTGUI {
#if VSTGUI_ENABLE_DEPRECATED_METHODS
bool CSwitchBase::useLegacyIndexCalculation = false;
#endif
//------------------------------------------------------------------------
CSwitchBase::CSwitchBase (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background)
: CControl (size, listener, tag, background)
{
setDefaultValue (0.f);
setWantsFocus (true);
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
CSwitchBase::CSwitchBase (const CRect& size, IControlListener* listener, int32_t tag,
int32_t subPixmaps, CCoord heightOfOneImage, int32_t iMaxPositions,
CBitmap* background, const CPoint& offset)
: CControl (size, listener, tag, background), offset (offset)
{
setNumSubPixmaps (subPixmaps);
setHeightOfOneImage (heightOfOneImage);
setDefaultValue (0.f);
setWantsFocus (true);
}
#endif
//------------------------------------------------------------------------
CSwitchBase::CSwitchBase (const CSwitchBase& other) : CControl (other)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
offset = other.offset;
setNumSubPixmaps (other.subPixmaps);
setHeightOfOneImage (other.heightOfOneImage);
#endif
setWantsFocus (true);
}
//------------------------------------------------------------------------
int32_t CSwitchBase::normalizedToIndex (float norm) const
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
{
return getMultiFrameBitmapIndex (*mfb, norm);
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
#include "../private/disabledeprecatedmessage.h"
if (useLegacyIndexCalculation)
#include "../private/enabledeprecatedmessage.h"
return static_cast<int32_t> (norm * (getNumSubPixmaps () - 1) + 0.5f);
return normalizedToSteps (norm, getNumSubPixmaps () - 1);
#else
return 0;
#endif
}
//------------------------------------------------------------------------
float CSwitchBase::indexToNormalized (int32_t index) const
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
{
return getNormValueFromMultiFrameBitmapIndex (*mfb, static_cast<uint16_t> (index));
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
return static_cast<float> (index) / static_cast<float> (getNumSubPixmaps () - 1);
#else
return 0.f;
#endif
}
//------------------------------------------------------------------------
void CSwitchBase::draw (CDrawContext* pContext)
{
if (auto bitmap = getDrawBackground ())
{
float norm = getValueNormalized ();
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
auto frameIndex = getMultiFrameBitmapIndex (*mfb, norm);
if (inverseBitmap)
frameIndex = getInverseIndex (*mfb, frameIndex);
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
}
else
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
if (inverseBitmap)
norm = 1.f - norm;
// source position in bitmap
CPoint where (0, heightOfOneImage * normalizedToIndex (norm));
bitmap->draw (pContext, getViewSize (), where);
#else
bitmap->draw (pContext, getViewSize ());
#endif
}
}
setDirty (false);
}
//------------------------------------------------------------------------
bool CSwitchBase::sizeToFit ()
{
if (auto bitmap = getDrawBackground ())
{
CRect vs (getViewSize ());
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
vs.setSize (mfb->getFrameSize ());
}
else
{
vs.setWidth (bitmap->getWidth ());
#if VSTGUI_ENABLE_DEPRECATED_METHODS
vs.setHeight (getHeightOfOneImage ());
#else
vs.setHeight (bitmap->getHeight ());
#endif
}
setViewSize (vs);
setMouseableArea (vs);
return true;
}
return false;
}
//------------------------------------------------------------------------
CMouseEventResult CSwitchBase::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (!(buttons & kLButton))
return kMouseEventNotHandled;
coef = calculateCoef ();
beginEdit ();
mouseStartValue = getValue ();
return onMouseMoved (where, buttons);
}
//------------------------------------------------------------------------
CMouseEventResult CSwitchBase::onMouseUp (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
endEdit ();
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CSwitchBase::onMouseCancel ()
{
if (isEditing ())
{
value = mouseStartValue;
if (isDirty ())
{
valueChanged ();
invalid ();
}
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CSwitchBase::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
float norm = calcNormFromPoint (where);
if (inverseBitmap)
norm = 1.f - norm;
value = getMin () + norm * (getMax () - getMin ());
bounceValue ();
if (isDirty ())
{
valueChanged ();
invalid ();
}
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
void CSwitchBase::setInverseBitmap (bool state)
{
if (inverseBitmap != state)
{
inverseBitmap = state;
invalid ();
}
}
//------------------------------------------------------------------------
// CVerticalSwitch
//------------------------------------------------------------------------
/*! @class CVerticalSwitch
Define a switch with a given number of positions, the current position is defined by the position
of the last click on this object (the object is divided in its height by the number of position).
Each position has its subbitmap, each subbitmap is stacked in the given handle bitmap.
By clicking Alt+Left Mouse the default value is used.
Use a CMultiFrameBitmap for its background bitmap.
*/
//------------------------------------------------------------------------
/**
* CVerticalSwitch constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background the switch bitmap
* @param offset unused
*/
//------------------------------------------------------------------------
CVerticalSwitch::CVerticalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background)
: CSwitchBase (size, listener, tag, background)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
heightOfOneImage = size.getHeight ();
setNumSubPixmaps (
background ? static_cast<int32_t> (background->getHeight () / heightOfOneImage) : 0);
#endif
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
/**
* CVerticalSwitch constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param subPixmaps number of sub bitmaps in background
* @param heightOfOneImage height of one sub bitmap
* @param iMaxPositions TODO
* @param background the switch bitmap
* @param offset unused
*/
//------------------------------------------------------------------------
CVerticalSwitch::CVerticalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
int32_t subPixmaps, CCoord heightOfOneImage,
int32_t iMaxPositions, CBitmap* background, const CPoint& offset)
: CSwitchBase (size, listener, tag, subPixmaps, heightOfOneImage, iMaxPositions, background, offset)
{
}
#endif
//------------------------------------------------------------------------
CVerticalSwitch::CVerticalSwitch (const CVerticalSwitch& v)
: CSwitchBase (v)
{
}
//------------------------------------------------------------------------
double CVerticalSwitch::calculateCoef () const
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
{
return mfb->getFrameSize ().y / static_cast<double> (getMultiFrameBitmapRangeLength (*mfb));
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
return static_cast<double> (heightOfOneImage) / static_cast<double> (getNumSubPixmaps ());
#else
return 1.;
#endif
}
//------------------------------------------------------------------------
float CVerticalSwitch::calcNormFromPoint (const CPoint& where) const
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
{
return static_cast<int32_t> ((where.y - getViewSize ().top) / getCoef ()) /
static_cast<float> (getMultiFrameBitmapRangeLength (*mfb) - 1);
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
return static_cast<int32_t> ((where.y - getViewSize ().top) / getCoef ()) /
static_cast<float> (getNumSubPixmaps () - 1);
#else
return 0.f;
#endif
}
//------------------------------------------------------------------------
void CVerticalSwitch::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type != EventType::KeyDown || event.modifiers.empty () == false)
return;
float norm = getValueNormalized ();
int32_t currentIndex = normalizedToIndex (norm);
if (event.virt == VirtualKey::Up && currentIndex > 0)
{
--currentIndex;
norm = indexToNormalized (currentIndex);
value = (getMax () - getMin ()) * norm + getMin ();
bounceValue ();
}
if (event.virt == VirtualKey::Down && norm < 1.f)
{
++currentIndex;
norm = indexToNormalized (currentIndex);
value = (getMax () - getMin ()) * norm + getMin ();
bounceValue ();
}
if (isDirty ())
{
invalid ();
beginEdit ();
valueChanged ();
endEdit ();
event.consumed = true;
}
}
//------------------------------------------------------------------------
// CHorizontalSwitch
//------------------------------------------------------------------------
/*! @class CHorizontalSwitch
Same as the CVerticalSwitch but horizontal.
Use a CMultiFrameBitmap for its background bitmap.
*/
//------------------------------------------------------------------------
/**
* CHorizontalSwitch constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background the bitmap of the switch
* @param offset unused
*/
//------------------------------------------------------------------------
CHorizontalSwitch::CHorizontalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background)
: CSwitchBase (size, listener, tag, background)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
heightOfOneImage = size.getWidth ();
setNumSubPixmaps (background ? (int32_t) (background->getWidth () / heightOfOneImage) : 0);
#endif
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
/**
* CHorizontalSwitch constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param subPixmaps number of sub bitmaps in background
* @param heightOfOneImage height of one sub bitmap
* @param iMaxPositions ignored
* @param background the switch bitmap
* @param offset unused
*/
//------------------------------------------------------------------------
CHorizontalSwitch::CHorizontalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
int32_t subPixmaps, CCoord heightOfOneImage,
int32_t iMaxPositions, CBitmap* background,
const CPoint& offset)
: CSwitchBase (size, listener, tag, subPixmaps, heightOfOneImage, iMaxPositions, background, offset)
{
}
#endif
//------------------------------------------------------------------------
CHorizontalSwitch::CHorizontalSwitch (const CHorizontalSwitch& v)
: CSwitchBase (v)
{
}
//------------------------------------------------------------------------
double CHorizontalSwitch::calculateCoef () const
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
{
return mfb->getFrameSize ().x / static_cast<double> (getMultiFrameBitmapRangeLength (*mfb));
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
return getDrawBackground ()->getWidth () / static_cast<double> (getNumSubPixmaps ());
#else
return 1.;
#endif
}
//------------------------------------------------------------------------
float CHorizontalSwitch::calcNormFromPoint (const CPoint& where) const
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (getDrawBackground ()))
{
return static_cast<int32_t> ((where.x - getViewSize ().left) / getCoef ()) /
static_cast<float> (getMultiFrameBitmapRangeLength (*mfb) - 1);
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
return static_cast<int32_t> ((where.x - getViewSize ().left) / getCoef ()) /
static_cast<float> (getNumSubPixmaps () - 1);
#else
return 0.f;
#endif
}
//------------------------------------------------------------------------
void CHorizontalSwitch::onKeyboardEvent(KeyboardEvent &event)
{
if (event.type != EventType::KeyDown || event.modifiers.empty () == false)
return;
float norm = getValueNormalized ();
int32_t currentIndex = normalizedToIndex (norm);
if (event.virt == VirtualKey::Left && currentIndex > 0)
{
--currentIndex;
norm = indexToNormalized (currentIndex);
value = (getMax () - getMin ()) * norm + getMin ();
bounceValue ();
}
if (event.virt == VirtualKey::Right && norm < 1.f)
{
++currentIndex;
norm = indexToNormalized (currentIndex);
value = (getMax () - getMin ()) * norm + getMin ();
bounceValue ();
}
if (isDirty ())
{
invalid ();
beginEdit ();
valueChanged ();
endEdit ();
event.consumed = true;
}
}
//------------------------------------------------------------------------
// CRockerSwitch
//------------------------------------------------------------------------
/*! @class CRockerSwitch
Define a rocker switch with 3 states using 3 subbitmaps.
One click on its leftside, then the first subbitmap is displayed.
One click on its rightside, then the third subbitmap is displayed.
When the mouse button is relaxed, the second subbitmap is framed. */
//------------------------------------------------------------------------
/**
* CRockerSwitch constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background bitmap with 3 stacked images of the rocker switch
* @param style
*/
//------------------------------------------------------------------------
CRockerSwitch::CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background, const int32_t style)
: CControl (size, listener, tag, background), style (style), resetValueTimer (nullptr)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
setNumSubPixmaps (3);
setHeightOfOneImage (size.getHeight ());
#endif
setWantsFocus (true);
setMin (-1.f);
setMax (1.f);
setValue ((getMax () - getMin ()) / 2.f + getMin ());
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//------------------------------------------------------------------------
/**
* CRockerSwitch constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param background bitmap with 3 stacked images of the rocker switch
* @param offset
* @param style
*/
//------------------------------------------------------------------------
CRockerSwitch::CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background, const CPoint &offset, const int32_t style)
: CControl (size, listener, tag, background)
, offset (offset)
, style (style)
, resetValueTimer (nullptr)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
setNumSubPixmaps (3);
setHeightOfOneImage (size.getHeight ());
#endif
setWantsFocus (true);
setMin (-1.f);
setMax (1.f);
setValue ((getMax () - getMin ()) / 2.f + getMin ());
}
//------------------------------------------------------------------------
/**
* CRockerSwitch constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param heightOfOneImage height of one image in pixel
* @param background bitmap with 3 stacked images of the rocker switch
* @param offset
* @param style
*/
//------------------------------------------------------------------------
CRockerSwitch::CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag, CCoord heightOfOneImage, CBitmap* background, const CPoint &offset, const int32_t style)
: CControl (size, listener, tag, background)
, offset (offset)
, style (style)
, resetValueTimer (nullptr)
{
setNumSubPixmaps (3);
setHeightOfOneImage (heightOfOneImage);
setWantsFocus (true);
setMin (-1.f);
setMax (1.f);
setValue ((getMax () - getMin ()) / 2.f + getMin ());
}
#endif
//------------------------------------------------------------------------
CRockerSwitch::CRockerSwitch (const CRockerSwitch& v)
: CControl (v), style (v.style), resetValueTimer (nullptr)
{
#if VSTGUI_ENABLE_DEPRECATED_METHODS
offset = v.offset;
setHeightOfOneImage (v.heightOfOneImage);
#endif
setWantsFocus (true);
}
//------------------------------------------------------------------------
CRockerSwitch::~CRockerSwitch () noexcept
{
if (resetValueTimer)
resetValueTimer->forget ();
}
//------------------------------------------------------------------------
void CRockerSwitch::draw (CDrawContext *pContext)
{
if (auto bitmap = getDrawBackground ())
{
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
uint16_t frameIndex = getMultiFrameBitmapIndex (*mfb, getValueNormalized ());
mfb->drawFrame (pContext, frameIndex, getViewSize ().getTopLeft ());
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint where (offset.x, offset.y);
if (value == getMax ())
where.y += 2 * heightOfOneImage;
else if (value == (getMax () - getMin ()) / 2.f + getMin ())
where.y += heightOfOneImage;
bitmap->draw (pContext, getViewSize (), where);
#else
bitmap->draw (pContext, getViewSize ());
#endif
}
setDirty (false);
}
//------------------------------------------------------------------------
CMouseEventResult CRockerSwitch::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (!(buttons & kLButton))
return kMouseEventNotHandled;
mouseStartValue = value;
beginEdit ();
return onMouseMoved (where, buttons);
}
//------------------------------------------------------------------------
CMouseEventResult CRockerSwitch::onMouseUp (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
value = (getMax () - getMin ()) / 2.f + getMin ();
if (isDirty ())
invalid ();
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CRockerSwitch::onMouseCancel ()
{
if (isEditing ())
{
value = mouseStartValue;
if (isDirty ())
{
valueChanged ();
invalid ();
}
endEdit ();
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
CMouseEventResult CRockerSwitch::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
if (isEditing ())
{
CCoord width_2 = getViewSize ().getWidth () / 2;
CCoord height_2 = getViewSize ().getHeight () / 2;
if (style & kHorizontal)
{
if (where.x >= getViewSize ().left && where.y >= getViewSize ().top &&
where.x <= (getViewSize ().left + width_2) && where.y <= getViewSize ().bottom)
value = getMin ();
else if (where.x >= (getViewSize ().left + width_2) && where.y >= getViewSize ().top &&
where.x <= getViewSize ().right && where.y <= getViewSize ().bottom)
value = getMax ();
else
value = mouseStartValue;
}
else
{
if (where.x >= getViewSize ().left && where.y >= getViewSize ().top &&
where.x <= getViewSize ().right && where.y <= (getViewSize ().top + height_2))
value = getMin ();
else if (where.x >= getViewSize ().left && where.y >= (getViewSize ().top + height_2) &&
where.x <= getViewSize ().right && where.y <= getViewSize ().bottom)
value = getMax ();
else
value = mouseStartValue;
}
if (isDirty ())
{
valueChanged ();
invalid ();
}
}
return kMouseEventHandled;
}
//------------------------------------------------------------------------
void CRockerSwitch::onKeyboardEvent (KeyboardEvent& event)
{
if (event.modifiers.empty () == false)
return;
if (event.type == EventType::KeyDown)
{
if (style & kHorizontal &&
(event.virt == VirtualKey::Left || event.virt == VirtualKey::Right))
{
value = event.virt == VirtualKey::Left ? getMin () : getMax ();
invalid ();
beginEdit ();
valueChanged ();
event.consumed = true;
}
if (style & kVertical && (event.virt == VirtualKey::Up || event.virt == VirtualKey::Down))
{
value = event.virt == VirtualKey::Up ? getMin () : getMax ();
invalid ();
beginEdit ();
valueChanged ();
event.consumed = true;
}
}
else if (event.type == EventType::KeyUp)
{
if ((style & kHorizontal &&
(event.virt == VirtualKey::Left || event.virt == VirtualKey::Right)) ||
(style & kVertical && (event.virt == VirtualKey::Up || event.virt == VirtualKey::Down)))
{
value = (getMax () - getMin ()) / 2.f + getMin ();
invalid ();
valueChanged ();
endEdit ();
event.consumed = true;
}
}
}
//------------------------------------------------------------------------
void CRockerSwitch::onMouseWheelEvent (MouseWheelEvent& event)
{
auto distance = event.deltaY;
if (distance == 0.)
return;
if (distance > 0)
value = getMin ();
else
value = getMax ();
if (isDirty ())
{
invalid ();
if (!isEditing ())
beginEdit ();
valueChanged ();
}
if (resetValueTimer == nullptr)
resetValueTimer = new CVSTGUITimer (this, 200);
resetValueTimer->stop ();
resetValueTimer->start ();
event.consumed = true;
}
//------------------------------------------------------------------------
CMessageResult CRockerSwitch::notify (CBaseObject* sender, IdStringPtr message)
{
if (sender == resetValueTimer)
{
float newValue = (getMax () - getMin ()) / 2.f + getMin ();
if (value != newValue)
{
value = newValue;
if (!isEditing ())
beginEdit ();
valueChanged ();
endEdit ();
setDirty (true);
}
resetValueTimer->forget ();
resetValueTimer = nullptr;
return kMessageNotified;
}
return CControl::notify (sender, message);
}
//-----------------------------------------------------------------------------------------------
bool CRockerSwitch::sizeToFit ()
{
if (auto bitmap = getDrawBackground ())
{
CRect vs (getViewSize ());
if (auto mfb = dynamic_cast<CMultiFrameBitmap*> (bitmap))
{
vs.setSize (mfb->getFrameSize ());
}
else
{
vs.setWidth (bitmap->getWidth ());
#if VSTGUI_ENABLE_DEPRECATED_METHODS
vs.setHeight (getHeightOfOneImage ());
#else
vs.setHeight (bitmap->getHeight ());
#endif
}
setViewSize (vs);
setMouseableArea (vs);
return true;
}
return false;
}
} // VSTGUI
@@ -0,0 +1,190 @@
// 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
#pragma once
#include "ccontrol.h"
#include "../cbitmap.h"
#include <algorithm>
namespace VSTGUI {
//-----------------------------------------------------------------------------
class CSwitchBase : public CControl,
public MultiFrameBitmapView<CSwitchBase>
#if VSTGUI_ENABLE_DEPRECATED_METHODS
,
public IMultiBitmapControl
#endif
{
public:
void setInverseBitmap (bool state);
bool getInverseBitmap () const { return inverseBitmap; }
protected:
CSwitchBase (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background);
CSwitchBase (const CSwitchBase& other);
~CSwitchBase () noexcept override = default;
void draw (CDrawContext*) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
bool sizeToFit () override;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CSwitchBase (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
CCoord heightOfOneImage, int32_t iMaxPositions, CBitmap* background,
const CPoint& offset = CPoint (0, 0));
void setNumSubPixmaps (int32_t numSubPixmaps) override
{
IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps);
invalid ();
}
const CPoint& getOffset () const { return offset; }
#endif
double getCoef () const { return coef; }
int32_t normalizedToIndex (float norm) const;
float indexToNormalized (int32_t index) const;
virtual double calculateCoef () const = 0;
virtual float calcNormFromPoint (const CPoint& where) const = 0;
VSTGUI_DEPRECATED_MSG (static bool useLegacyIndexCalculation;
, "Use CMultiFrameBitmap::normalizedValueToFrameIndex() instead")
private:
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint offset {};
#endif
double coef;
float mouseStartValue;
bool inverseBitmap{false};
};
//-----------------------------------------------------------------------------
// CVerticalSwitch Declaration
//! @brief a vertical switch control
/// @ingroup controls uses_multi_frame_bitmaps
//-----------------------------------------------------------------------------
class CVerticalSwitch : public CSwitchBase
{
public:
CVerticalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background);
CVerticalSwitch (const CVerticalSwitch& vswitch);
void onKeyboardEvent (KeyboardEvent& event) override;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CVerticalSwitch (const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
CCoord heightOfOneImage, int32_t iMaxPositions, CBitmap* background,
const CPoint& offset = CPoint (0, 0));
#endif
CLASS_METHODS(CVerticalSwitch, CControl)
protected:
~CVerticalSwitch () noexcept override = default;
double calculateCoef () const override;
float calcNormFromPoint (const CPoint& where) const override;
};
//-----------------------------------------------------------------------------
// CHorizontalSwitch Declaration
//! @brief a horizontal switch control
/// @ingroup controls uses_multi_frame_bitmaps
//-----------------------------------------------------------------------------
class CHorizontalSwitch : public CSwitchBase
{
public:
CHorizontalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
CBitmap* background);
CHorizontalSwitch (const CHorizontalSwitch& hswitch);
void onKeyboardEvent (KeyboardEvent& event) override;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CHorizontalSwitch (const CRect& size, IControlListener* listener, int32_t tag,
int32_t subPixmaps, CCoord heightOfOneImage, int32_t iMaxPositions,
CBitmap* background, const CPoint& offset = CPoint (0, 0));
#endif
CLASS_METHODS(CHorizontalSwitch, CControl)
protected:
~CHorizontalSwitch () noexcept override = default;
double calculateCoef () const override;
float calcNormFromPoint (const CPoint& where) const override;
};
//-----------------------------------------------------------------------------
// CRockerSwitch Declaration
//! @brief a switch control with 3 sub bitmaps
/// @ingroup controls use_multi_frame_bitmaps
//-----------------------------------------------------------------------------
class CRockerSwitch : public CControl,
public MultiFrameBitmapView<CRockerSwitch>
#if VSTGUI_ENABLE_DEPRECATED_METHODS
,
public IMultiBitmapControl
#endif
{
private:
enum StyleEnum
{
StyleHorizontal = 0,
StyleVertical,
};
public:
enum Style
{
kHorizontal = 1 << StyleHorizontal,
kVertical = 1 << StyleVertical,
};
CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background,
const int32_t style = kHorizontal);
CRockerSwitch (const CRockerSwitch& rswitch);
void draw (CDrawContext*) override;
void onMouseWheelEvent (MouseWheelEvent& event) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseCancel () override;
void onKeyboardEvent (KeyboardEvent& event) override;
bool sizeToFit () override;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background,
const CPoint& offset, const int32_t style = kHorizontal);
CRockerSwitch (const CRect& size, IControlListener* listener, int32_t tag,
CCoord heightOfOneImage, CBitmap* background,
const CPoint& offset = CPoint (0, 0), const int32_t style = kHorizontal);
void setNumSubPixmaps (int32_t numSubPixmaps) override { IMultiBitmapControl::setNumSubPixmaps (numSubPixmaps); invalid (); }
#endif
CLASS_METHODS(CRockerSwitch, CControl)
protected:
~CRockerSwitch () noexcept override;
CMessageResult notify (CBaseObject* sender, IdStringPtr message) override;
#if VSTGUI_ENABLE_DEPRECATED_METHODS
CPoint offset {};
#endif
int32_t style;
CVSTGUITimer* resetValueTimer;
private:
float mouseStartValue;
};
} // VSTGUI
@@ -0,0 +1,421 @@
// 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 "ctextedit.h"
#include "itexteditlistener.h"
#include "../cframe.h"
#include "../cdrawcontext.h"
#include "../events.h"
#include "../platform/iplatformframe.h"
#include <cassert>
namespace VSTGUI {
//------------------------------------------------------------------------
// CTextEdit
//------------------------------------------------------------------------
/*! @class CTextEdit
Define a rectangle view where a text-value can be displayed and edited with a given font and color.
The user can specify its convert function (from char to char). The text-value is centered in the given rect.
A bitmap can be used as background.
*/
//------------------------------------------------------------------------
/**
* CTextEdit constructor.
* @param size the size of this view
* @param listener the listener
* @param tag the control tag
* @param txt the initial text as c string (UTF-8 encoded)
* @param background the background bitmap
* @param style the display style (see CParamDisplay for styles)
*/
//------------------------------------------------------------------------
CTextEdit::CTextEdit (const CRect& size, IControlListener* listener, int32_t tag, UTF8StringPtr txt,
CBitmap* background, const int32_t style)
: CTextLabel (size, txt, background, style)
{
this->listener = listener;
this->tag = tag;
setWantsFocus (true);
}
//------------------------------------------------------------------------
CTextEdit::CTextEdit (const CTextEdit& v)
: CTextLabel (v)
, bWasReturnPressed (false)
, stringToValueFunction (v.stringToValueFunction)
, immediateTextChange (v.immediateTextChange)
, secureStyle (v.secureStyle)
, platformFont (v.platformFont)
, placeholderString (v.placeholderString)
{
setWantsFocus (true);
}
//------------------------------------------------------------------------
CTextEdit::~CTextEdit () noexcept
{
listener = nullptr;
vstgui_assert (platformControl == nullptr);
}
//------------------------------------------------------------------------
void CTextEdit::setStringToValueFunction (const StringToValueFunction& stringToValueFunc)
{
stringToValueFunction = stringToValueFunc;
}
//------------------------------------------------------------------------
void CTextEdit::setStringToValueFunction (StringToValueFunction&& stringToValueFunc)
{
stringToValueFunction = std::move (stringToValueFunc);
}
//------------------------------------------------------------------------
void CTextEdit::setImmediateTextChange (bool state)
{
immediateTextChange = state;
}
//------------------------------------------------------------------------
void CTextEdit::setSecureStyle (bool state)
{
if (secureStyle != state)
{
secureStyle = state;
if (platformControl)
{
}
}
}
//------------------------------------------------------------------------
bool CTextEdit::getSecureStyle () const
{
return secureStyle;
}
//------------------------------------------------------------------------
void CTextEdit::registerTextEditListener (ITextEditListener* listener)
{
textEditListeners.add (listener);
}
//------------------------------------------------------------------------
void CTextEdit::unregisterTextEditListener (ITextEditListener* listener)
{
textEditListeners.remove (listener);
}
//------------------------------------------------------------------------
void CTextEdit::setValue (float val)
{
CTextLabel::setValue (val);
bool converted = false;
std::string string;
if (valueToStringFunction)
converted = valueToStringFunction (getValue (), string, this);
if (!converted)
{
char tmp[255];
char precisionStr[10];
snprintf (precisionStr, 10, "%%.%hhuf", valuePrecision);
snprintf (tmp, 255, precisionStr, getValue ());
string = tmp;
}
if (converted)
{
CTextLabel::setText (UTF8String (std::move (string)));
if (platformControl)
platformControl->setText (getText ());
}
else
setText (UTF8String (std::move (string)));
}
//------------------------------------------------------------------------
void CTextEdit::setText (const UTF8String& txt)
{
if (stringToValueFunction)
{
float val = getValue ();
if (stringToValueFunction (txt, val, this))
{
CTextLabel::setValue (val);
if (valueToStringFunction)
{
std::string string;
valueToStringFunction (getValue (), string, this);
CTextLabel::setText (UTF8String (std::move (string)));
if (platformControl)
platformControl->setText (getText ());
return;
}
}
}
CTextLabel::setText (txt);
if (platformControl)
platformControl->setText (getText ());
}
//------------------------------------------------------------------------
void CTextEdit::valueChanged ()
{
if (stringToValueFunction)
CTextLabel::valueChanged ();
CParamDisplay::valueChanged ();
}
//------------------------------------------------------------------------
void CTextEdit::setPlaceholderString (const UTF8String& str)
{
placeholderString = str;
}
//------------------------------------------------------------------------
void CTextEdit::draw (CDrawContext *pContext)
{
if (platformControl)
{
drawBack (pContext);
if (!platformControl->drawsPlaceholder () && !placeholderString.empty () &&
platformControl->getText ().empty ())
{
pContext->saveGlobalState ();
pContext->setGlobalAlpha (pContext->getGlobalAlpha () * 0.5f);
drawPlatformText (pContext, placeholderString);
pContext->restoreGlobalState ();
}
setDirty (false);
return;
}
drawBack (pContext);
if (text.empty ())
{
if (!placeholderString.empty ())
{
pContext->saveGlobalState ();
pContext->setGlobalAlpha (pContext->getGlobalAlpha () * 0.5f);
drawPlatformText (pContext, placeholderString);
pContext->restoreGlobalState ();
}
}
else if (getSecureStyle ())
{
constexpr auto bulletCharacter = "\xE2\x80\xA2";
UTF8String str;
for (auto i = 0u; i < text.length (); ++i)
str += bulletCharacter;
drawPlatformText (pContext, str);
}
else
CTextLabel::draw (pContext);
setDirty (false);
}
//------------------------------------------------------------------------
CMouseEventResult CTextEdit::onMouseDown (CPoint& where, const CButtonState& buttons)
{
if (buttons & kLButton)
{
if (getFrame ()->getFocusView () != this)
{
if (isDoubleClickStyle ())
{
if (!(buttons & kDoubleClick))
return kMouseEventNotHandled;
}
takeFocus ();
return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
}
}
return kMouseEventNotHandled;
}
//------------------------------------------------------------------------
void CTextEdit::onKeyboardEvent (KeyboardEvent& event)
{
if (!platformControl || event.type != EventType::KeyDown)
return;
if (event.virt == VirtualKey::Escape)
{
bWasReturnPressed = false;
platformControl->setText (text);
getFrame ()->setFocusView (nullptr);
looseFocus ();
event.consumed = true;
}
else if (event.virt == VirtualKey::Return)
{
bWasReturnPressed = true;
getFrame ()->setFocusView (nullptr);
looseFocus ();
event.consumed = true;
}
}
//------------------------------------------------------------------------
CFontRef CTextEdit::platformGetFont () const
{
CFontRef font = getFont ();
CCoord fontSize = font->getSize ();
fontSize *= getGlobalTransform ().m11;
if (fontSize == font->getSize ())
return font;
platformFont = makeOwned<CFontDesc> (*font);
platformFont->setSize (fontSize);
return platformFont;
}
//------------------------------------------------------------------------
CRect CTextEdit::platformGetSize () const
{
return translateToGlobal (getViewSize ());
}
//------------------------------------------------------------------------
CRect CTextEdit::platformGetVisibleSize () const
{
return translateToGlobal (getVisibleViewSize ());
}
//------------------------------------------------------------------------
void CTextEdit::platformLooseFocus (bool returnPressed)
{
remember ();
bWasReturnPressed = returnPressed;
if (getFrame ()->getFocusView () == this)
getFrame ()->setFocusView (nullptr);
forget ();
}
//------------------------------------------------------------------------
void CTextEdit::platformOnKeyboardEvent (KeyboardEvent& event)
{
dynamic_cast<IPlatformFrameCallback*> (getFrame ())->platformOnEvent (event);
if (event.consumed)
return;
if (event.virt == VirtualKey::Return)
{
platformLooseFocus (true);
event.consumed = true;
}
else if (event.virt == VirtualKey::Escape)
{
platformLooseFocus (false);
event.consumed = true;
}
}
//------------------------------------------------------------------------
void CTextEdit::platformTextDidChange ()
{
if (platformControl && immediateTextChange)
updateText (platformControl);
}
//------------------------------------------------------------------------
bool CTextEdit::platformIsSecureTextEdit ()
{
return getSecureStyle ();
}
//------------------------------------------------------------------------
void CTextEdit::parentSizeChanged ()
{
if (platformControl)
platformControl->updateSize ();
}
//------------------------------------------------------------------------
void CTextEdit::setViewSize (const CRect& newSize, bool invalid)
{
CTextLabel::setViewSize (newSize, invalid);
if (platformControl)
platformControl->updateSize ();
}
//------------------------------------------------------------------------
void CTextEdit::createPlatformTextEdit ()
{
if (platformControl)
return;
bWasReturnPressed = false;
platformControl = getFrame ()->getPlatformFrame ()->createPlatformTextEdit (this);
textEditListeners.forEach (
[this] (ITextEditListener* l) { l->onTextEditPlatformControlTookFocus (this); });
}
//------------------------------------------------------------------------
bool CTextEdit::wantsFocus () const
{
if (isDoubleClickStyle () && !platformControl)
return false;
return CTextLabel::wantsFocus ();
}
//------------------------------------------------------------------------
void CTextEdit::takeFocus ()
{
if (!getFrame ())
return;
createPlatformTextEdit ();
if (getFrame()->getFocusView () != this)
getFrame()->setFocusView (this);
CTextLabel::takeFocus ();
invalid ();
}
//------------------------------------------------------------------------
void CTextEdit::looseFocus ()
{
if (platformControl == nullptr)
return;
CBaseObjectGuard guard (this);
auto _platformControl = std::move (platformControl);
updateText (_platformControl);
_platformControl = nullptr;
textEditListeners.forEach (
[this] (ITextEditListener* l) { l->onTextEditPlatformControlLostFocus (this); });
// if you want to destroy the text edit do it with the loose focus message
CView* receiver = getParentView () ? getParentView () : getFrame ();
while (receiver)
{
if (receiver->notify (this, kMsgLooseFocus) == kMessageNotified)
break;
receiver = receiver->getParentView ();
}
CTextLabel::looseFocus ();
invalid ();
}
//------------------------------------------------------------------------
void CTextEdit::updateText (IPlatformTextEdit* pte)
{
auto newText = pte->getText ();
if (newText != getText ())
{
beginEdit ();
setText (newText);
CParamDisplay::valueChanged ();
endEdit ();
}
}
} // VSTGUI
@@ -0,0 +1,122 @@
// 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
#pragma once
#include "ctextlabel.h"
#include "../dispatchlist.h"
#include "../platform/iplatformtextedit.h"
#include <functional>
namespace VSTGUI {
using CTextEditStringToValueProc = bool (*) (UTF8StringPtr txt, float& result, void* userData);
//-----------------------------------------------------------------------------
// CTextEdit Declaration
//! @brief a text edit control
/// @ingroup controls
//-----------------------------------------------------------------------------
class CTextEdit : public CTextLabel, public IPlatformTextEditCallback
{
private:
enum StyleEnum
{
StyleDoubleClick = CParamDisplay::LastStyle,
};
public:
using PlatformTextEditPtr = SharedPointer<IPlatformTextEdit>;
CTextEdit (const CRect& size, IControlListener* listener, int32_t tag, UTF8StringPtr txt = nullptr, CBitmap* background = nullptr, const int32_t style = 0);
CTextEdit (const CTextEdit& textEdit);
enum Style
{
kDoubleClickStyle = 1 << StyleDoubleClick,
};
bool isDoubleClickStyle () const { return hasBit (getStyle (), kDoubleClickStyle); }
//-----------------------------------------------------------------------------
/// @name CTextEdit Methods
//-----------------------------------------------------------------------------
//@{
using StringToValueUserData = CTextEdit;
using StringToValueFunction = std::function<bool(UTF8StringPtr txt, float& result, CTextEdit* textEdit)>;
void setStringToValueFunction (const StringToValueFunction& stringToValueFunc);
void setStringToValueFunction (StringToValueFunction&& stringToValueFunc);
/** enable/disable immediate text change behaviour */
virtual void setImmediateTextChange (bool state);
/** get immediate text change behaviour */
bool getImmediateTextChange () const { return immediateTextChange; }
/** enable/disable secure style */
void setSecureStyle (bool state);
/** get secure style */
bool getSecureStyle () const;
virtual void setPlaceholderString (const UTF8String& str);
const UTF8String& getPlaceholderString () const { return placeholderString; }
void registerTextEditListener (ITextEditListener* listener);
void unregisterTextEditListener (ITextEditListener* listener);
//@}
// overrides
void setText (const UTF8String& txt) override;
void valueChanged () override;
void setValue (float val) override;
void setTextRotation (double angle) override { } // not supported
void draw (CDrawContext* pContext) override;
CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
void onKeyboardEvent (KeyboardEvent& event) override;
void takeFocus () override;
void looseFocus () override;
bool wantsFocus () const override;
void setViewSize (const CRect& newSize, bool invalid = true) override;
void parentSizeChanged () override;
bool bWasReturnPressed {false};
PlatformTextEditPtr getPlatformTextEdit () const { return platformControl; }
CLASS_METHODS(CTextEdit, CParamDisplay)
protected:
~CTextEdit () noexcept override;
void createPlatformTextEdit ();
void updateText (IPlatformTextEdit* pte);
CColor platformGetBackColor () const override { return getBackColor (); }
CColor platformGetFontColor () const override { return getFontColor (); }
CFontRef platformGetFont () const override;
CHoriTxtAlign platformGetHoriTxtAlign () const override { return getHoriAlign (); }
const UTF8String& platformGetText () const override { return text; }
const UTF8String& platformGetPlaceholderText () const override { return placeholderString; }
CRect platformGetSize () const override;
CRect platformGetVisibleSize () const override;
CPoint platformGetTextInset () const override { return getTextInset (); }
void platformLooseFocus (bool returnPressed) override;
void platformOnKeyboardEvent (KeyboardEvent& event) override;
void platformTextDidChange () override;
bool platformIsSecureTextEdit () override;
PlatformTextEditPtr platformControl;
StringToValueFunction stringToValueFunction;
bool immediateTextChange {false};
bool secureStyle {false};
mutable SharedPointer<CFontDesc> platformFont;
UTF8String placeholderString;
DispatchList<ITextEditListener*> textEditListeners;
};
} // VSTGUI
@@ -0,0 +1,483 @@
// 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 "ctextlabel.h"
#include "../platform/iplatformfont.h"
#include "../cdrawmethods.h"
#include "../cdrawcontext.h"
#include <sstream>
namespace VSTGUI {
//------------------------------------------------------------------------
// CTextLabel
//------------------------------------------------------------------------
/*! @class CTextLabel
*/
//------------------------------------------------------------------------
/**
* CTextLabel constructor.
* @param size the size of this view
* @param txt the initial text as c string (UTF-8 encoded)
* @param background the background bitmap
* @param style the display style (see CParamDisplay for styles)
*/
//------------------------------------------------------------------------
CTextLabel::CTextLabel (const CRect& size, UTF8StringPtr txt, CBitmap* background, const int32_t style)
: CParamDisplay (size, background, style)
, textTruncateMode (kTruncateNone)
{
setText (txt);
}
//------------------------------------------------------------------------
CTextLabel::CTextLabel (const CTextLabel& v)
: CParamDisplay (v)
, textTruncateMode (v.textTruncateMode)
{
setText (v.getText ());
}
//------------------------------------------------------------------------
void CTextLabel::registerTextLabelListener (ITextLabelListener* listener)
{
if (!listeners)
listeners = std::unique_ptr<TextLabelListenerList> (new TextLabelListenerList ());
listeners->add (listener);
}
//------------------------------------------------------------------------
void CTextLabel::unregisterTextLabelListener (ITextLabelListener* listener)
{
if (listeners)
listeners->remove (listener);
}
//------------------------------------------------------------------------
void CTextLabel::setText (const UTF8String& txt)
{
if (text == txt)
return;
text = txt;
if (textTruncateMode != kTruncateNone)
calculateTruncatedText ();
setDirty (true);
}
//------------------------------------------------------------------------
void CTextLabel::setTextTruncateMode (TextTruncateMode mode)
{
if (textTruncateMode != mode)
{
textTruncateMode = mode;
calculateTruncatedText ();
}
}
//------------------------------------------------------------------------
void CTextLabel::calculateTruncatedText ()
{
if (textRotation != 0.) // currently truncation is only supported when not rotated
{
truncatedText = "";
return;
}
if (!(textTruncateMode == kTruncateNone || text.empty () || fontID == nullptr || fontID->getPlatformFont () == nullptr || fontID->getPlatformFont ()->getPainter () == nullptr))
{
CDrawMethods::TextTruncateMode mode = textTruncateMode == kTruncateHead ? CDrawMethods::kTextTruncateHead : CDrawMethods::kTextTruncateTail;
truncatedText = CDrawMethods::createTruncatedText (mode, text, fontID, getWidth () - getTextInset ().x * 2.);
if (truncatedText == text)
truncatedText.clear ();
if (listeners)
{
listeners->forEach (
[this] (ITextLabelListener* l) { l->onTextLabelTruncatedTextChanged (this); });
}
}
else if (!truncatedText.empty ())
truncatedText.clear ();
}
//------------------------------------------------------------------------
const UTF8String& CTextLabel::getText () const
{
return text;
}
//------------------------------------------------------------------------
void CTextLabel::draw (CDrawContext *pContext)
{
drawBack (pContext);
drawPlatformText (pContext, truncatedText.empty () ? text : truncatedText);
setDirty (false);
}
//------------------------------------------------------------------------
bool CTextLabel::sizeToFit ()
{
if (fontID == nullptr || fontID->getPlatformFont () == nullptr || fontID->getPlatformFont ()->getPainter () == nullptr)
return false;
CCoord width = fontID->getPlatformFont ()->getPainter ()->getStringWidth (nullptr, text.getPlatformString (), true);
if (width > 0)
{
width += (getTextInset ().x * 2.);
CRect newSize = getViewSize ();
newSize.setWidth (width);
setViewSize (newSize);
setMouseableArea (newSize);
return true;
}
return false;
}
//------------------------------------------------------------------------
void CTextLabel::setViewSize (const CRect& rect, bool invalid)
{
CRect current (getViewSize ());
CParamDisplay::setViewSize (rect, invalid);
if (textTruncateMode != kTruncateNone && current.getWidth () != getWidth ())
{
calculateTruncatedText ();
}
}
//------------------------------------------------------------------------
void CTextLabel::drawStyleChanged ()
{
if (textTruncateMode != kTruncateNone)
{
calculateTruncatedText ();
}
CParamDisplay::drawStyleChanged ();
}
//------------------------------------------------------------------------
void CTextLabel::valueChanged ()
{
if (valueToStringFunction)
{
std::string string;
if (valueToStringFunction (getValue (), string, this))
setText (UTF8String (std::move (string)));
}
CParamDisplay::valueChanged ();
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
CMultiLineTextLabel::CMultiLineTextLabel (const CRect& size)
: CTextLabel (size)
{
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::setValue (float val)
{
CTextLabel::setValue (val);
if (valueToStringFunction)
{
std::string string;
if (valueToStringFunction (value, string, this))
setText (UTF8String (string));
}
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::setTextTruncateMode (TextTruncateMode)
{
// not supported on multi line labels
CTextLabel::setTextTruncateMode (kTruncateNone);
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::setLineLayout (LineLayout layout)
{
if (lineLayout == layout)
return;
lineLayout = layout;
lines.clear ();
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::setAutoHeight (bool state)
{
if (autoHeight == state)
return;
autoHeight = state;
if (autoHeight && isAttached ())
{
if (lines.empty ())
recalculateLines (nullptr);
recalculateHeight ();
}
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::setVerticalCentered (bool state)
{
if (verticalCentered == state)
return;
verticalCentered = state;
lines.clear ();
}
//------------------------------------------------------------------------
CCoord CMultiLineTextLabel::getMaxLineWidth ()
{
if (lines.empty () && getText ().empty () == false)
recalculateLines (nullptr);
CCoord maxWidth {};
for (const auto& line : lines)
{
if (line.r.getWidth () > maxWidth)
maxWidth = line.r.getWidth ();
}
return maxWidth;
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::drawRect (CDrawContext* pContext, const CRect& updateRect)
{
if (getText ().empty () == false && lines.empty ())
recalculateLines (pContext);
drawBack (pContext);
CRect newClip (updateRect);
newClip.inset (getTextInset ());
ConcatClip clip (*pContext, newClip);
newClip = clip.get ();
pContext->setDrawMode (kAntiAliasing);
pContext->setFont (getFont ());
newClip.offsetInverse (getViewSize().getTopLeft ());
CDrawContext::Transform t (*pContext, CGraphicsTransform ().translate (getViewSize ().getTopLeft ()));
if (style & kShadowText)
{
CDrawContext::Transform t2 (*pContext, CGraphicsTransform ().translate (shadowTextOffset));
pContext->setFontColor (getShadowColor ());
for (const auto& line : lines)
{
if (line.r.rectOverlap (newClip))
pContext->drawString (line.str.getPlatformString (), line.r, getHoriAlign (), getAntialias ());
}
}
pContext->setFontColor (getFontColor ());
for (const auto& line : lines)
{
if (line.r.rectOverlap (newClip))
pContext->drawString (line.str.getPlatformString (), line.r, getHoriAlign (), getAntialias ());
else if (line.r.bottom > newClip.bottom)
break;
}
setDirty (false);
}
//------------------------------------------------------------------------
bool CMultiLineTextLabel::sizeToFit ()
{
return false;
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::setText (const UTF8String& txt)
{
if (getText () == txt)
return;
CTextLabel::setText (txt);
lines.clear ();
if (autoHeight && isAttached ())
{
recalculateLines (nullptr);
recalculateHeight ();
}
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::recalculateHeight ()
{
auto viewSize = getViewSize ();
if (lines.empty ())
viewSize.setHeight (0.);
else
{
auto lastLine = lines.back ().r;
viewSize.setHeight (lastLine.bottom + getTextInset ().y);
}
CTextLabel::setViewSize (viewSize);
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::setViewSize (const CRect& rect, bool invalid)
{
auto viewSize = getViewSize ();
auto normRect = rect;
viewSize.originize ();
normRect.originize ();
if (viewSize != normRect)
{
if (lineLayout != LineLayout::clip ||
(lineLayout == LineLayout::clip &&
viewSize.getHeight () != normRect.getHeight ()))
{
lines.clear ();
}
}
CTextLabel::setViewSize (rect, invalid);
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::drawStyleChanged ()
{
lines.clear ();
CTextLabel::drawStyleChanged ();
}
//------------------------------------------------------------------------
inline bool isLineBreakSeparator (char32_t c)
{
switch (c)
{
case '-': return true;
case '_': return true;
case '/': return true;
case '\\': return true;
case '.': return true;
case ',': return true;
case ':': return true;
case ';': return true;
case '?': return true;
case '!': return true;
case '*': return true;
case '+': return true;
case '&': return true;
}
return false;
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::calculateWrapLine (CDrawContext* context,
std::pair<UTF8String, double>& element,
const IFontPainter* const& fontPainter, double lineHeight,
double lineWidth, double maxWidth, const CPoint& textInset,
CCoord& y)
{
auto start = element.first.begin ();
auto lastSeparator = start;
auto pos = start;
while (pos != element.first.end () && *pos != 0)
{
if (isspace (*pos))
lastSeparator = pos;
else if (isLineBreakSeparator (*pos))
lastSeparator = ++pos;
if (pos == element.first.end ())
break;
auto tmpEnd = pos;
UTF8String tmp ({start.base (), (++tmpEnd).base ()});
auto width = fontPainter->getStringWidth (
context ? context->getPlatformDeviceContext () : nullptr, tmp.getPlatformString ());
if (width > maxWidth)
{
if (lastSeparator == element.first.end ())
lastSeparator = pos;
if (start == lastSeparator)
lastSeparator = pos;
lines.emplace_back (
Line {CRect (textInset.x, y, lineWidth, y + lineHeight + textInset.y),
UTF8String ({start.base (), lastSeparator.base ()})});
y += lineHeight;
pos = lastSeparator;
start = pos;
if (isspace (*start))
++start;
lastSeparator = element.first.end ();
}
++pos;
}
if (start != element.first.end ())
{
lines.emplace_back (Line {CRect (textInset.x, y, lineWidth, y + lineHeight + textInset.y),
UTF8String ({start.base (), element.first.end ().base ()})});
y += lineHeight;
}
}
//------------------------------------------------------------------------
void CMultiLineTextLabel::recalculateLines (CDrawContext* context)
{
const auto& font = getFont ()->getPlatformFont ();
const auto& fontPainter = getFont ()->getFontPainter ();
auto ascent = font->getAscent ();
auto descent = font->getDescent ();
auto leading = font->getLeading ();
auto lineHeight = ascent + descent + leading;
const auto& textInset = getTextInset ();
auto maxWidth = getWidth () - (textInset.x * 2);
std::vector<std::pair<UTF8String, CCoord>> elements;
std::stringstream stream (getText ().getString ());
std::string line;
while (std::getline (stream, line, '\n'))
{
UTF8String str (std::move (line));
auto width = fontPainter->getStringWidth (
context ? context->getPlatformDeviceContext () : nullptr, str.getPlatformString ());
elements.emplace_back (std::move (str), width);
}
CCoord y = textInset.y;
auto lineWidth = getWidth () - textInset.x;
for (auto& element : elements)
{
if (lineLayout == LineLayout::clip)
{
lines.emplace_back (Line {
CRect (textInset.x, y, element.second + textInset.x, y + lineHeight + textInset.y),
std::move (element.first)});
}
else
{
if (element.second > maxWidth)
{
if (lineLayout == LineLayout::truncate)
{
element.first = CDrawMethods::createTruncatedText (
CDrawMethods::kTextTruncateTail, element.first, fontID, maxWidth);
}
else // wrap
{
calculateWrapLine (context, element, fontPainter, lineHeight, lineWidth,
maxWidth, textInset, y);
continue;
}
}
lines.emplace_back (
Line {CRect (textInset.x, y, lineWidth, y + lineHeight + textInset.y),
std::move (element.first)});
}
y += lineHeight;
}
if (getVerticalCentered () && !lines.empty ())
{
auto maxHeight = lines.back ().r.bottom;
auto offset = ((getHeight () - textInset.y) - maxHeight) / 2.;
if (offset > 0)
{
for (auto& l : lines)
l.r.offset (0, offset);
}
}
}
} // VSTGUI
@@ -0,0 +1,143 @@
// 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
#pragma once
#include "cparamdisplay.h"
#include "itextlabellistener.h"
#include "../dispatchlist.h"
#include "../cstring.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CLabel Declaration
//! @brief a text label
/// @ingroup controls
//-----------------------------------------------------------------------------
class CTextLabel : public CParamDisplay
{
public:
CTextLabel (const CRect& size, UTF8StringPtr txt = nullptr, CBitmap* background = nullptr, const int32_t style = 0);
CTextLabel (const CTextLabel& textLabel);
//-----------------------------------------------------------------------------
/// @name CTextLabel Methods
//-----------------------------------------------------------------------------
//@{
/** set text */
virtual void setText (const UTF8String& txt);
/** read only access to text */
virtual const UTF8String& getText () const;
enum TextTruncateMode {
/** no characters will be removed */
kTruncateNone = 0,
/** characters will be removed from the beginning of the text */
kTruncateHead,
/** characters will be removed from the end of the text */
kTruncateTail
};
/** set text truncate mode */
virtual void setTextTruncateMode (TextTruncateMode mode);
/** get text truncate mode */
TextTruncateMode getTextTruncateMode () const { return textTruncateMode; }
/** get the truncated text */
const UTF8String& getTruncatedText () const { return truncatedText; }
/** register a text label listener */
void registerTextLabelListener (ITextLabelListener* listener);
/** unregister a text label listener */
void unregisterTextLabelListener (ITextLabelListener* listener);
//@}
void draw (CDrawContext* pContext) override;
bool sizeToFit () override;
void setViewSize (const CRect& rect, bool invalid = true) override;
void drawStyleChanged () override;
void valueChanged () override;
CLASS_METHODS(CTextLabel, CParamDisplay)
protected:
~CTextLabel () noexcept override = default;
void freeText ();
void calculateTruncatedText ();
#if VSTGUI_ENABLE_DEPRECATED_METHODS
bool onWheel (const CPoint& where, const CMouseWheelAxis& axis, const float& distance, const CButtonState& buttons) override { return false; }
#endif
TextTruncateMode textTruncateMode;
UTF8String text;
UTF8String truncatedText;
using TextLabelListenerList = DispatchList<ITextLabelListener*>;
std::unique_ptr<TextLabelListenerList> listeners;
};
//-----------------------------------------------------------------------------
/** Multi line text label
* @ingroup new_in_4_5
*/
class CMultiLineTextLabel : public CTextLabel
{
public:
CMultiLineTextLabel (const CRect& size);
CMultiLineTextLabel (const CMultiLineTextLabel&) = default;
enum class LineLayout {
/** clip lines overflowing the view size width */
clip,
/** truncate lines overflowing the view size width */
truncate,
/** wrap overflowing words to next line */
wrap
};
void setLineLayout (LineLayout layout);
LineLayout getLineLayout () const { return lineLayout; }
/** automatically resize the view according to the contents (only the height)
* @param state on or off
*/
void setAutoHeight (bool state);
/** returns true if this view resizes itself according to the contents */
bool getAutoHeight () const { return autoHeight; }
/** draw the lines vertical centered
* @param state on or off
*/
void setVerticalCentered (bool state);
/** returns true if the view draws the lines vertically centered */
bool getVerticalCentered () const { return verticalCentered; }
/** return the maximum line width of all lines */
CCoord getMaxLineWidth ();
void drawRect (CDrawContext* pContext, const CRect& updateRect) override;
bool sizeToFit () override;
void setText (const UTF8String& txt) override;
void setViewSize (const CRect& rect, bool invalid = true) override;
void setTextTruncateMode (TextTruncateMode mode) override;
void setValue (float val) override;
private:
void drawStyleChanged () override;
void calculateWrapLine (CDrawContext *context, std::pair<UTF8String, double> &element, const IFontPainter *const &fontPainter, double lineHeight, double lineWidth, double maxWidth, const CPoint &textInset, CCoord &y);
void recalculateLines (CDrawContext* context);
void recalculateHeight ();
bool autoHeight {false};
bool verticalCentered {false};
LineLayout lineLayout {LineLayout::clip};
struct Line
{
CRect r;
UTF8String str;
};
using Lines = std::vector<Line>;
Lines lines;
};
} // VSTGUI
@@ -0,0 +1,155 @@
// 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 "cvumeter.h"
#include "../coffscreencontext.h"
#include "../cbitmap.h"
#include "../cvstguitimer.h"
#include <list>
namespace VSTGUI {
//------------------------------------------------------------------------
// CVuMeter
//------------------------------------------------------------------------
/**
* CVuMeter constructor.
* @param size the size of this view
* @param onBitmap TODO
* @param offBitmap TODO
* @param nbLed TODO
* @param style kHorizontal or kVertical
*/
//------------------------------------------------------------------------
CVuMeter::CVuMeter (const CRect& size, CBitmap* onBitmap, CBitmap* offBitmap, int32_t nbLed,
Style style)
: CControl (size, nullptr, 0), offBitmap (nullptr), nbLed (nbLed), style (style)
{
setDecreaseStepValue (0.1f);
setOnBitmap (onBitmap);
setOffBitmap (offBitmap);
rectOn (size.left, size.top, size.right, size.bottom);
rectOff (size.left, size.top, size.right, size.bottom);
setWantsIdle (true);
}
//------------------------------------------------------------------------
CVuMeter::CVuMeter (const CVuMeter& v)
: CControl (v)
, offBitmap (nullptr)
, nbLed (v.nbLed)
, style (v.style)
, decreaseValue (v.decreaseValue)
, rectOn (v.rectOn)
, rectOff (v.rectOff)
{
setOffBitmap (v.offBitmap);
setWantsIdle (true);
}
//------------------------------------------------------------------------
CVuMeter::~CVuMeter () noexcept
{
setOnBitmap (nullptr);
setOffBitmap (nullptr);
}
//------------------------------------------------------------------------
void CVuMeter::setViewSize (const CRect& newSize, bool invalid)
{
CControl::setViewSize (newSize, invalid);
rectOn = getViewSize ();
rectOff = getViewSize ();
}
//------------------------------------------------------------------------
bool CVuMeter::sizeToFit ()
{
if (getDrawBackground ())
{
CRect vs (getViewSize ());
vs.setWidth (getDrawBackground ()->getWidth ());
vs.setHeight (getDrawBackground ()->getHeight ());
setViewSize (vs);
setMouseableArea (vs);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void CVuMeter::setOffBitmap (CBitmap* bitmap)
{
if (offBitmap)
offBitmap->forget ();
offBitmap = bitmap;
if (offBitmap)
offBitmap->remember ();
}
//------------------------------------------------------------------------
void CVuMeter::setDirty (bool state)
{
CView::setDirty (state);
}
//------------------------------------------------------------------------
void CVuMeter::onIdle ()
{
if (getOldValue () != value)
invalid ();
}
//------------------------------------------------------------------------
void CVuMeter::draw (CDrawContext *_pContext)
{
if (!getOnBitmap ())
return;
CRect _rectOn (rectOn);
CRect _rectOff (rectOff);
CPoint pointOn;
CPoint pointOff;
CDrawContext *pContext = _pContext;
bounceValue ();
float newValue = getOldValue () - decreaseValue;
if (newValue < value)
newValue = value;
setOldValue (newValue);
newValue = (newValue - getMin ()) / getRange (); // normalize
if (style == Style::kHorizontal)
{
auto tmp = (CCoord)(((int32_t)(nbLed * newValue + 0.5f) / (float)nbLed) * getOnBitmap ()->getWidth ());
pointOff (tmp, 0);
_rectOff.left += tmp;
_rectOn.right = tmp + rectOn.left;
}
else
{
auto tmp = (CCoord)(((int32_t)(nbLed * (1.f - newValue) + 0.5f) / (float)nbLed) * getOnBitmap ()->getHeight ());
pointOn (0, tmp);
_rectOff.bottom = tmp + rectOff.top;
_rectOn.top += tmp;
}
if (getOffBitmap ())
{
getOffBitmap ()->draw (pContext, _rectOff, pointOff);
}
getOnBitmap ()->draw (pContext, _rectOn, pointOn);
setDirty (false);
}
} // VSTGUI
@@ -0,0 +1,74 @@
// 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
#pragma once
#include "../enumbitset.h"
#include "ccontrol.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
// CVuMeter Declaration
//!
/// @ingroup controls
//-----------------------------------------------------------------------------
class CVuMeter : public CControl
{
public:
enum class Style : int32_t
{
kHorizontal,
kVertical,
};
CVuMeter (const CRect& size, CBitmap* onBitmap, CBitmap* offBitmap, int32_t nbLed,
Style style = Style::kVertical);
CVuMeter (const CVuMeter& vuMeter);
//-----------------------------------------------------------------------------
/// @name CVuMeter Methods
//-----------------------------------------------------------------------------
//@{
float getDecreaseStepValue () const { return decreaseValue; }
virtual void setDecreaseStepValue (float value) { decreaseValue = value; }
virtual CBitmap* getOnBitmap () const { return getBackground (); }
virtual CBitmap* getOffBitmap () const { return offBitmap; }
virtual void setOnBitmap (CBitmap* bitmap) { setBackground (bitmap); }
virtual void setOffBitmap (CBitmap* bitmap);
int32_t getNbLed () const { return nbLed; }
void setNbLed (int32_t nb) { nbLed = nb; invalid (); }
void setStyle (Style newStyle)
{
style = newStyle;
invalid ();
}
Style getStyle () const { return style; }
//@}
// overrides
void setDirty (bool state) override;
void draw (CDrawContext* pContext) override;
void setViewSize (const CRect& newSize, bool invalid = true) override;
bool sizeToFit () override;
void onIdle () override;
CLASS_METHODS(CVuMeter, CControl)
protected:
~CVuMeter () noexcept override;
CBitmap* offBitmap;
int32_t nbLed;
Style style;
float decreaseValue;
CRect rectOn;
CRect rectOff;
};
} // VSTGUI
@@ -0,0 +1,225 @@
// 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 "cxypad.h"
#include "../cdrawcontext.h"
#include "../events.h"
namespace VSTGUI {
//------------------------------------------------------------------------
CXYPad::CXYPad (const CRect& size)
: CParamDisplay (size)
, stopTrackingOnMouseExit (false)
{
CParamDisplay::setMax (2.f);
}
//------------------------------------------------------------------------
void CXYPad::setHandleBitmap (CBitmap* bitmap)
{
handle = bitmap;
invalid ();
}
//------------------------------------------------------------------------
CBitmap* CXYPad::getHandleBitmap () const
{
return handle;
}
//------------------------------------------------------------------------
void CXYPad::draw (CDrawContext* context)
{
drawBack (context);
auto width = getWidth () - getRoundRectRadius ();
auto height = getHeight () - getRoundRectRadius ();
float x, y;
calculateXY (getValue (), x, y);
CRect r (x*width, y*height, x*width, y*height);
if (auto bitmap = getHandleBitmap ())
{
auto bitmapSize = bitmap->getSize ();
r.extend (bitmapSize.x / 2., bitmapSize.y / 2.);
r.offset (getViewSize ().left + getRoundRectRadius () / 2.,
getViewSize ().top + getRoundRectRadius () / 2.);
bitmap->draw (context, r);
}
else
{
r.extend (getRoundRectRadius () / 2., getRoundRectRadius () / 2.);
r.offset (getViewSize ().left + getRoundRectRadius () / 2.,
getViewSize ().top + getRoundRectRadius () / 2.);
context->setFillColor (getFontColor ());
context->setDrawMode (kAntiAliasing);
context->drawEllipse (r, kDrawFilled);
}
setDirty (false);
}
//------------------------------------------------------------------------
void CXYPad::drawBack (CDrawContext* context, CBitmap* newBack)
{
CParamDisplay::drawBack (context);
}
//------------------------------------------------------------------------
void CXYPad::onMouseDownEvent (MouseDownEvent& event)
{
if (event.buttonState.isLeft ())
{
invalidMouseWheelEditTimer (this);
mouseStartValue = getValue ();
mouseChangeStartPoint = event.mousePosition;
mouseChangeStartPoint.offset (-getViewSize ().left - getRoundRectRadius () / 2.,
-getViewSize ().top - getRoundRectRadius () / 2.);
beginEdit ();
onMouseMove (event);
}
}
//------------------------------------------------------------------------
void CXYPad::onMouseUpEvent (MouseUpEvent& event)
{
if (isEditing ())
{
endEdit ();
event.consumed = true;
}
}
//------------------------------------------------------------------------
void CXYPad::onMouseCancelEvent (MouseCancelEvent &event)
{
if (isEditing ())
{
value = mouseStartValue;
if (isDirty ())
{
valueChanged ();
invalid ();
}
endEdit ();
event.consumed = true;
}
}
//------------------------------------------------------------------------
void CXYPad::onMouseMoveEvent (MouseMoveEvent& event)
{
if (event.buttonState.isLeft () && isEditing ())
{
onMouseMove (event);
}
}
//------------------------------------------------------------------------
void CXYPad::onMouseMove (MouseDownUpMoveEvent& event)
{
auto where = event.mousePosition;
if (stopTrackingOnMouseExit)
{
if (!hitTest (where, event))
{
endEdit ();
event.ignoreFollowUpMoveAndUpEvents (true);
event.consumed = true;
return;
}
}
float x, y;
CCoord width = getWidth() - getRoundRectRadius ();
CCoord height = getHeight() - getRoundRectRadius ();
where.offset (-getViewSize ().left - getRoundRectRadius () / 2.,
-getViewSize ().top - getRoundRectRadius () / 2.);
x = (float)(where.x / width);
y = (float)(where.y / height);
boundValues (x, y);
setValue (calculateValue (x, y));
if (isDirty ())
{
valueChanged ();
invalid ();
}
lastMouseChangePoint = where;
event.consumed = true;
}
//------------------------------------------------------------------------
void CXYPad::onMouseWheelEvent (MouseWheelEvent& event)
{
float x, y;
calculateXY (getValue (), x, y);
auto distanceX = static_cast<float> (event.deltaX) * getWheelInc ();
auto distanceY = static_cast<float> (event.deltaY) * getWheelInc ();
if (event.flags & MouseWheelEvent::DirectionInvertedFromDevice)
{
distanceX *= -1.f;
distanceY *= -1.f;
}
if (event.modifiers.has (ModifierKey::Shift))
{
distanceX *= 0.1f;
distanceY *= 0.1f;
}
x += distanceX;
y += distanceY;
boundValues (x, y);
onMouseWheelEditing (this);
setValue (calculateValue (x, y));
if (isDirty ())
{
invalid ();
valueChanged ();
}
event.consumed = true;
}
//------------------------------------------------------------------------
void CXYPad::onKeyboardEvent (KeyboardEvent& event)
{
if (event.type != EventType::KeyDown)
return;
if (event.virt == VirtualKey::Escape)
{
if (isEditing ())
{
onMouseCancel ();
event.consumed = true;
}
}
}
//------------------------------------------------------------------------
void CXYPad::boundValues (float& x, float& y)
{
if (x < 0.f)
x = 0.f;
else if (x > 1.f)
x = 1.f;
if (y < 0.f)
y = 0.f;
else if (y > 1.f)
y = 1.f;
}
//------------------------------------------------------------------------
void CXYPad::setDefaultValue (float val)
{
CControl::setDefaultValue (calculateValue (val, val));
}
//------------------------------------------------------------------------
void CXYPad::setDefaultValues (float x, float y)
{
CControl::setDefaultValue (calculateValue (x, y));
}
} // VSTGUI
@@ -0,0 +1,68 @@
// 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
#pragma once
#include "cparamdisplay.h"
#include "../cbitmap.h"
#include <cmath>
namespace VSTGUI {
//------------------------------------------------------------------------
class CXYPad : public CParamDisplay, protected CMouseWheelEditingSupport
{
public:
explicit CXYPad (const CRect& size = CRect (0, 0, 0, 0));
void setStopTrackingOnMouseExit (bool state) { stopTrackingOnMouseExit = state; }
bool getStopTrackingOnMouseExit () const { return stopTrackingOnMouseExit; }
void setHandleBitmap (CBitmap* bitmap);
CBitmap* getHandleBitmap () const;
void draw (CDrawContext* context) override;
void drawBack (CDrawContext* pContext, CBitmap* newBack = nullptr) override;
void onMouseDownEvent (MouseDownEvent& event) override;
void onMouseUpEvent (MouseUpEvent& event) override;
void onMouseMoveEvent (MouseMoveEvent& event) override;
void onMouseCancelEvent (MouseCancelEvent& event) override;
void onMouseWheelEvent (MouseWheelEvent& event) override;
void onKeyboardEvent (KeyboardEvent& event) override;
/** set default value so that x and y default to val */
void setDefaultValue (float val) override;
/** set default value for x and y */
void setDefaultValues (float x, float y);
static float calculateValue (float x, float y)
{
x = std::floor (x * 1000.f + 0.5f) * 0.001f;
y = std::floor (y * 1000.f + 0.5f) * 0.0000001f;
return x + y;
}
static void calculateXY (float value, float& x, float& y)
{
x = std::floor (value * 1000.f + 0.5f) * 0.001f;
y = std::floor ((value - x) * 10000000.f + 0.5f) * 0.001f;
}
protected:
void onMouseMove (MouseDownUpMoveEvent& event);
void setMin (float val) override { }
void setMax (float val) override { }
void boundValues (float& x, float& y);
float mouseStartValue;
CPoint mouseChangeStartPoint;
CPoint lastMouseChangePoint;
bool stopTrackingOnMouseExit;
SharedPointer<CBitmap> handle;
};
} // VSTGUI
@@ -0,0 +1,33 @@
// 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
#pragma once
#include "../vstguifwd.h"
namespace VSTGUI {
//------------------------------------------------------------------------
/** Command menu item target
* @ingroup new_in_4_7
*/
class ICommandMenuItemTarget : public virtual IReference
{
public:
/** called before the item is shown to validate its state */
virtual bool validateCommandMenuItem (CCommandMenuItem* item) = 0;
/** called when the item was selected */
virtual bool onCommandMenuItemSelected (CCommandMenuItem* item) = 0;
};
//------------------------------------------------------------------------
class CommandMenuItemTargetAdapter : public ICommandMenuItemTarget
{
public:
bool validateCommandMenuItem (CCommandMenuItem* item) override { return false; }
bool onCommandMenuItemSelected (CCommandMenuItem* item) override { return false; }
};
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,26 @@
// 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
#pragma once
#include "../vstguifwd.h"
#include "../cbuttonstate.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
class IControlListener
{
public:
virtual ~IControlListener () noexcept = default;
virtual void valueChanged (CControl* pControl) = 0;
/** return 1 if you want the control to not handle it, otherwise 0 */
virtual int32_t controlModifierClicked (CControl* pControl, CButtonState button) { return 0; }
virtual void controlBeginEdit (CControl* pControl) {}
virtual void controlEndEdit (CControl* pControl) {}
virtual void controlTagWillChange (CControl* pControl) {}
virtual void controlTagDidChange (CControl* pControl) {}
};
} // VSTGUI
@@ -0,0 +1,50 @@
// 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
#pragma once
#include "../vstguifwd.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
/** Option menu listener
* @ingroup new_in_4_7
*/
class IOptionMenuListener
{
public:
/** called before the menu pops up */
virtual void onOptionMenuPrePopup (COptionMenu* menu) = 0;
/** called after the menu pops up */
virtual void onOptionMenuPostPopup (COptionMenu* menu) = 0;
/** called when the platform optionmenu returns the result and before the value of the option
* menu is set.
* @ingroup new_in_4_10
*
* @param menu the listened menu
* @param selectedMenu the menu containing the selected item
* @param selectedIndex the index of the selected item
* @return return true to prevent further propagating the call to other listeners and to
* prevent setting the value of the option menu
*/
virtual bool onOptionMenuSetPopupResult (COptionMenu* menu, COptionMenu* selectedMenu,
int32_t selectedIndex) = 0;
};
//-----------------------------------------------------------------------------
class OptionMenuListenerAdapter : public IOptionMenuListener
{
public:
void onOptionMenuPrePopup (COptionMenu* menu) override {}
void onOptionMenuPostPopup (COptionMenu* menu) override {}
bool onOptionMenuSetPopupResult (COptionMenu* menu, COptionMenu* selectedMenu,
int32_t selectedIndex) override
{
return false;
}
};
//------------------------------------------------------------------------
} // VSTGUI
@@ -0,0 +1,33 @@
// 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
#pragma once
#include "../vstguifwd.h"
namespace VSTGUI {
//------------------------------------------------------------------------
/** Listener for a text edit
* @ingroup new_in_4_10
*/
class ITextEditListener
{
public:
/** called when the native platform text edit control was created and started to listen for keyboard input. */
virtual void onTextEditPlatformControlTookFocus (CTextEdit* textEdit) = 0;
/** called when the natvie platform text edit control is going to be destroyed. */
virtual void onTextEditPlatformControlLostFocus (CTextEdit* textEdit) = 0;
};
//------------------------------------------------------------------------
class TextEditListenerAdapter : public ITextEditListener
{
public:
void onTextEditPlatformControlTookFocus (CTextEdit* textEdit) override {}
void onTextEditPlatformControlLostFocus (CTextEdit* textEdit) override {}
};
//------------------------------------------------------------------------
} // VSTGUI

Some files were not shown because too many files have changed in this diff Show More