git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
deps/juce
.gitattributesCMakeLists.txtDemoRunner.jucerCMakeLists.txtCMakeLists.txtCMakeLists.txtjucer_Project.cppjucer_Project.hCMakeLists.txt
.github
.gitignore.gitlab-ci.yml.gitrepoBREAKING-CHANGES.txtCMakeLists.txtChangeList.txtLICENSE.mdREADME.mddocs
examples
Assets
AudioLiveScrollingDisplay.h
Box2DTests
AddPair.hApplyForce.hBodyTypes.hBreakable.hBridge.hBulletTest.hCantilever.hCar.hChain.hCharacterCollision.hCollisionFiltering.hCollisionProcessing.hCompoundShapes.hConfined.hContinuousTest.hDistanceTest.hDominos.hDumpShell.hDynamicTreeTest.hEdgeShapes.hEdgeTest.hGears.hOneSidedPlatform.hPinball.hPolyCollision.hPolyShapes.hPrismatic.hPulleys.hPyramid.hRayCast.hRevolute.hRope.hRopeJoint.hSensorTest.hShapeEditing.hSliderCrank.hSphereStack.hTestEntries.cppTheoJansen.hTiles.hTimeOfImpact.hTumbler.hVaryingFriction.hVaryingRestitution.hVerticalStack.hWeb.h
DSPDemos_Common.hDemoUtilities.hNotifications
images
ic_stat_name.pngic_stat_name10.pngic_stat_name2.pngic_stat_name3.pngic_stat_name4.pngic_stat_name5.pngic_stat_name6.pngic_stat_name7.pngic_stat_name8.pngic_stat_name9.png
sounds
Purchases
Ed.pngEd0.oggEd1.oggEd2.oggFabian.pngFabian0.oggFabian1.oggFabian2.oggJB.pngJB0.oggJB1.oggJB2.oggJules.pngJules0.oggJules1.oggJules2.oggLukasz.pngLukasz0.oggLukasz1.oggLukasz2.oggRobot.pngRobot0.oggRobot1.oggRobot2.oggicon.png
Signing
WavefrontObjParser.hcassette_recorder.wavcello.wavdemo table data.xmlgoogle-services.jsonguitar_amp.wavicons.zipjuce_icon.pngjuce_icon_template.pngjuce_module_infoportmeirion.jpgpower.pngproaudio.pathreverb_ir.wavsinging.oggteapot.objtile_background.pngtreedemo.xmlAudio
AudioAppDemo.hAudioLatencyDemo.hAudioPlaybackDemo.hAudioRecordingDemo.hAudioSettingsDemo.hAudioSynthesiserDemo.hCMakeLists.txtMPEDemo.hMidiDemo.hPluckedStringsDemo.hSimpleFFTDemo.h
CMake
CMakeLists.txtDSP
CMakeLists.txtConvolutionDemo.hFIRFilterDemo.hGainDemo.hIIRFilterDemo.hOscillatorDemo.hOverdriveDemo.hSIMDRegisterDemo.hStateVariableFilterDemo.hWaveShaperTanhDemo.h
DemoRunner
Builds
Android
app
CMakeLists.txtbuild.gradle
build.gradlesrc
debug
res
values
main
AndroidManifest.xml
assets
AudioLiveScrollingDisplay.h
Box2DTests
AddPair.hApplyForce.hBodyTypes.hBreakable.hBridge.hBulletTest.hCantilever.hCar.hChain.hCharacterCollision.hCollisionFiltering.hCollisionProcessing.hCompoundShapes.hConfined.hContinuousTest.hDistanceTest.hDominos.hDumpShell.hDynamicTreeTest.hEdgeShapes.hEdgeTest.hGears.hOneSidedPlatform.hPinball.hPolyCollision.hPolyShapes.hPrismatic.hPulleys.hPyramid.hRayCast.hRevolute.hRope.hRopeJoint.hSensorTest.hShapeEditing.hSliderCrank.hSphereStack.hTestEntries.cppTheoJansen.hTiles.hTimeOfImpact.hTumbler.hVaryingFriction.hVaryingRestitution.hVerticalStack.hWeb.h
DSPDemos_Common.hDemoUtilities.hNotifications
images
ic_stat_name.pngic_stat_name10.pngic_stat_name2.pngic_stat_name3.pngic_stat_name4.pngic_stat_name5.pngic_stat_name6.pngic_stat_name7.pngic_stat_name8.pngic_stat_name9.png
sounds
Purchases
Ed.pngEd0.oggEd1.oggEd2.oggFabian.pngFabian0.oggFabian1.oggFabian2.oggJB.pngJB0.oggJB1.oggJB2.oggJules.pngJules0.oggJules1.oggJules2.oggLukasz.pngLukasz0.oggLukasz1.oggLukasz2.oggRobot.pngRobot0.oggRobot1.oggRobot2.oggicon.png
Signing
WavefrontObjParser.hcassette_recorder.wavcello.wavdemo table data.xmlgoogle-services.jsonguitar_amp.wavicons.zipjuce_icon.pngjuce_icon_template.pngjuce_module_infoportmeirion.jpgpower.pngproaudio.pathreverb_ir.wavsinging.oggteapot.objtile_background.pngtreedemo.xmlres
release
res
values
gradle
gradlewgradlew.batsettings.gradleLinuxMakefile
MacOSX
VisualStudio2015
VisualStudio2017
VisualStudio2019
iOS
App.entitlementsIcon.icnsInfo-App.plistLaunchScreen.storyboard
DemoRunner.xcodeproj
DemoRunner
Images.xcassets
AppIcon.appiconset
Contents.jsonIcon-29.pngIcon-29@2x.pngIcon-29@3x.pngIcon-60@2x.pngIcon-72.pngIcon-72@2x.pngIcon-76.pngIcon-76@2x.pngIcon-83.5@2x.pngIcon-@3x.pngIcon-AppStore-1024.pngIcon-Notification-20@2x.pngIcon-Notification-20@3x.pngIcon-Notifications-20.pngIcon-Notifications-20@2x.pngIcon-Small-1.pngIcon-Small-50.pngIcon-Small-50@2x.pngIcon-Small@2x-1.pngIcon-Spotlight-40.pngIcon-Spotlight-40@2x-1.pngIcon-Spotlight-40@2x.pngIcon-Spotlight-40@3x.pngIcon.pngIcon@2x.png
LaunchImage.launchimage
JuceLibraryCode
JuceHeader.hReadMe.txtinclude_juce_analytics.cppinclude_juce_audio_basics.cppinclude_juce_audio_basics.mminclude_juce_audio_devices.cppinclude_juce_audio_devices.mminclude_juce_audio_formats.cppinclude_juce_audio_formats.mminclude_juce_audio_processors.cppinclude_juce_audio_processors.mminclude_juce_audio_utils.cppinclude_juce_audio_utils.mminclude_juce_box2d.cppinclude_juce_core.cppinclude_juce_core.mminclude_juce_cryptography.cppinclude_juce_cryptography.mminclude_juce_data_structures.cppinclude_juce_data_structures.mminclude_juce_dsp.cppinclude_juce_dsp.mminclude_juce_events.cppinclude_juce_events.mminclude_juce_graphics.cppinclude_juce_graphics.mminclude_juce_gui_basics.cppinclude_juce_gui_basics.mminclude_juce_gui_extra.cppinclude_juce_gui_extra.mminclude_juce_opengl.cppinclude_juce_opengl.mminclude_juce_osc.cppinclude_juce_product_unlocking.cppinclude_juce_product_unlocking.mminclude_juce_video.cppinclude_juce_video.mm
Source
GUI
AccessibilityDemo.hAnimationAppDemo.hAnimationDemo.hBouncingBallWavetableDemo.hCMakeLists.txtCameraDemo.hCodeEditorDemo.hComponentDemo.hComponentTransformsDemo.hDialogsDemo.hFlexBoxDemo.hFontsDemo.hGraphicsDemo.hGridDemo.hHelloWorldDemo.hImagesDemo.hKeyMappingsDemo.hLookAndFeelDemo.hMDIDemo.hMenusDemo.hMultiTouchDemo.hOpenGLAppDemo.hOpenGLDemo.hOpenGLDemo2D.hPropertiesDemo.hVideoDemo.hWebBrowserDemo.hWidgetsDemo.hWindowsDemo.h
Plugins
AUv3SynthPluginDemo.hArpeggiatorPluginDemo.hAudioPluginDemo.hCMakeLists.txtDSPModulePluginDemo.hGainPluginDemo.hInterAppAudioEffectPluginDemo.hMidiLoggerPluginDemo.hMultiOutSynthPluginDemo.hNoiseGatePluginDemo.hReaperEmbeddedViewPluginDemo.hSamplerPluginDemo.hSurroundPluginDemo.h
extern
Utilities
extras
AudioPerformanceTest
AudioPerformanceTest.jucerInfo-App.plistLaunchScreen.storyboardCMakeLists.txt
Builds
Android
LinuxMakefile
MacOSX
VisualStudio2019
AudioPerformanceTest.slnAudioPerformanceTest_App.vcxprojAudioPerformanceTest_App.vcxproj.filtersresources.rc
iOS
AudioPerformanceTest.xcodeproj
AudioPerformanceTest
Images.xcassets
JuceLibraryCode
JuceHeader.hReadMe.txtinclude_juce_audio_basics.cppinclude_juce_audio_basics.mminclude_juce_audio_devices.cppinclude_juce_audio_devices.mminclude_juce_audio_formats.cppinclude_juce_audio_formats.mminclude_juce_audio_processors.cppinclude_juce_audio_processors.mminclude_juce_audio_utils.cppinclude_juce_audio_utils.mminclude_juce_core.cppinclude_juce_core.mminclude_juce_data_structures.cppinclude_juce_data_structures.mminclude_juce_events.cppinclude_juce_events.mminclude_juce_graphics.cppinclude_juce_graphics.mminclude_juce_gui_basics.cppinclude_juce_gui_basics.mminclude_juce_gui_extra.cppinclude_juce_gui_extra.mm
Source
AudioPluginHost
AudioPluginHost.jucerIcon.icnsInfo-App.plistLaunchScreen.storyboardCMakeLists.txt
Builds
Android
app
CMakeLists.txtbuild.gradle
build.gradlesrc
debug
res
values
main
AndroidManifest.xml
assets
AudioLiveScrollingDisplay.h
Box2DTests
AddPair.hApplyForce.hBodyTypes.hBreakable.hBridge.hBulletTest.hCantilever.hCar.hChain.hCharacterCollision.hCollisionFiltering.hCollisionProcessing.hCompoundShapes.hConfined.hContinuousTest.hDistanceTest.hDominos.hDumpShell.hDynamicTreeTest.hEdgeShapes.hEdgeTest.hGears.hOneSidedPlatform.hPinball.hPolyCollision.hPolyShapes.hPrismatic.hPulleys.hPyramid.hRayCast.hRevolute.hRope.hRopeJoint.hSensorTest.hShapeEditing.hSliderCrank.hSphereStack.hTestEntries.cppTheoJansen.hTiles.hTimeOfImpact.hTumbler.hVaryingFriction.hVaryingRestitution.hVerticalStack.hWeb.h
DSPDemos_Common.hDemoUtilities.hNotifications
images
ic_stat_name.pngic_stat_name10.pngic_stat_name2.pngic_stat_name3.pngic_stat_name4.pngic_stat_name5.pngic_stat_name6.pngic_stat_name7.pngic_stat_name8.pngic_stat_name9.png
sounds
Purchases
Ed.pngEd0.oggEd1.oggEd2.oggFabian.pngFabian0.oggFabian1.oggFabian2.oggJB.pngJB0.oggJB1.oggJB2.oggJules.pngJules0.oggJules1.oggJules2.oggLukasz.pngLukasz0.oggLukasz1.oggLukasz2.oggRobot.pngRobot0.oggRobot1.oggRobot2.oggicon.png
Signing
WavefrontObjParser.hcassette_recorder.wavcello.wavdemo table data.xmlgoogle-services.jsonguitar_amp.wavicons.zipjuce_icon.pngjuce_icon_template.pngjuce_module_infoportmeirion.jpgpower.pngproaudio.pathreverb_ir.wavsinging.oggteapot.objtile_background.pngtreedemo.xmlres
release
res
values
gradle
gradlewgradlew.batsettings.gradleLinuxMakefile
MacOSX
VisualStudio2015
AudioPluginHost.slnAudioPluginHost_App.vcxprojAudioPluginHost_App.vcxproj.filtersicon.icoresources.rc
VisualStudio2017
AudioPluginHost.slnAudioPluginHost_App.vcxprojAudioPluginHost_App.vcxproj.filtersicon.icoresources.rc
VisualStudio2019
AudioPluginHost.slnAudioPluginHost_App.vcxprojAudioPluginHost_App.vcxproj.filtersicon.icoresources.rc
iOS
AudioPluginHost.xcodeproj
AudioPluginHost
Images.xcassets
AppIcon.appiconset
Contents.jsonIcon-29.pngIcon-29@2x.pngIcon-29@3x.pngIcon-60@2x.pngIcon-72.pngIcon-72@2x.pngIcon-76.pngIcon-76@2x.pngIcon-83.5@2x.pngIcon-@3x.pngIcon-AppStore-1024.pngIcon-Notification-20@2x.pngIcon-Notification-20@3x.pngIcon-Notifications-20.pngIcon-Notifications-20@2x.pngIcon-Small-1.pngIcon-Small-50.pngIcon-Small-50@2x.pngIcon-Small@2x-1.pngIcon-Spotlight-40.pngIcon-Spotlight-40@2x-1.pngIcon-Spotlight-40@2x.pngIcon-Spotlight-40@3x.pngIcon.pngIcon@2x.png
LaunchImage.launchimage
JuceLibraryCode
JuceHeader.hReadMe.txtinclude_juce_audio_basics.cppinclude_juce_audio_basics.mminclude_juce_audio_devices.cppinclude_juce_audio_devices.mminclude_juce_audio_formats.cppinclude_juce_audio_formats.mminclude_juce_audio_processors.cppinclude_juce_audio_processors.mminclude_juce_audio_utils.cppinclude_juce_audio_utils.mminclude_juce_core.cppinclude_juce_core.mminclude_juce_cryptography.cppinclude_juce_cryptography.mminclude_juce_data_structures.cppinclude_juce_data_structures.mminclude_juce_dsp.cppinclude_juce_dsp.mminclude_juce_events.cppinclude_juce_events.mminclude_juce_graphics.cppinclude_juce_graphics.mminclude_juce_gui_basics.cppinclude_juce_gui_basics.mminclude_juce_gui_extra.cppinclude_juce_gui_extra.mminclude_juce_opengl.cppinclude_juce_opengl.mm
Source
BinaryBuilder
Build
CMake
JUCECheckAtomic.cmakeJUCEConfig.cmake.inJUCEHelperTargets.cmakeJUCEModuleSupport.cmakeJUCEUtils.cmakeLaunchScreen.storyboardPIPAudioProcessor.cpp.inPIPComponent.cpp.inPIPConsole.cpp.inRecentFilesMenuTemplate.nibUnityPluginGUIScript.cs.incopyDir.cmakejuce_runtime_arch_detection.cpp
CMakeLists.txtjuce_build_tools
juce_build_tools.cppjuce_build_tools.h
utils
juce_BinaryResourceFile.cppjuce_BinaryResourceFile.hjuce_BuildHelperFunctions.cppjuce_BuildHelperFunctions.hjuce_CppTokeniserFunctions.cppjuce_Entitlements.cppjuce_Entitlements.hjuce_Icons.cppjuce_Icons.hjuce_PlistOptions.cppjuce_PlistOptions.hjuce_ProjectType.hjuce_RelativePath.hjuce_ResourceFileHelpers.cppjuce_ResourceFileHelpers.hjuce_ResourceRc.cppjuce_ResourceRc.hjuce_VersionNumbers.cppjuce_VersionNumbers.h
juceaide
NetworkGraphicsDemo
Builds
Android
LinuxMakefile
MacOSX
VisualStudio2019
NetworkGraphicsDemo.slnNetworkGraphicsDemo_App.vcxprojNetworkGraphicsDemo_App.vcxproj.filtersresources.rc
iOS
Icon.icnsInfo-App.plistLaunchScreen.storyboard
NetworkGraphicsDemo.xcodeproj
NetworkGraphicsDemo
Images.xcassets
AppIcon.appiconset
Contents.jsonIcon-29.pngIcon-29@2x.pngIcon-29@3x.pngIcon-60@2x.pngIcon-72.pngIcon-72@2x.pngIcon-76.pngIcon-76@2x.pngIcon-83.5@2x.pngIcon-@3x.pngIcon-AppStore-1024.pngIcon-Notification-20@2x.pngIcon-Notification-20@3x.pngIcon-Notifications-20.pngIcon-Notifications-20@2x.pngIcon-Small-1.pngIcon-Small-50.pngIcon-Small-50@2x.pngIcon-Small@2x-1.pngIcon-Spotlight-40.pngIcon-Spotlight-40@2x-1.pngIcon-Spotlight-40@2x.pngIcon-Spotlight-40@3x.pngIcon.pngIcon@2x.png
LaunchImage.launchimage
JuceLibraryCode
BinaryData.cppBinaryData.hJuceHeader.hReadMe.txtinclude_juce_audio_basics.cppinclude_juce_audio_basics.mminclude_juce_audio_devices.cppinclude_juce_audio_devices.mminclude_juce_audio_formats.cppinclude_juce_audio_formats.mminclude_juce_audio_processors.cppinclude_juce_audio_processors.mminclude_juce_audio_utils.cppinclude_juce_audio_utils.mminclude_juce_core.cppinclude_juce_core.mminclude_juce_cryptography.cppinclude_juce_cryptography.mminclude_juce_data_structures.cppinclude_juce_data_structures.mminclude_juce_events.cppinclude_juce_events.mminclude_juce_graphics.cppinclude_juce_graphics.mminclude_juce_gui_basics.cppinclude_juce_gui_basics.mminclude_juce_gui_extra.cppinclude_juce_gui_extra.mminclude_juce_opengl.cppinclude_juce_opengl.mminclude_juce_osc.cpp
NetworkGraphicsDemo.jucerREADME.txtSource
Projucer
Builds
LinuxMakefile
MacOSX
VisualStudio2015
VisualStudio2017
VisualStudio2019
JuceLibraryCode
BinaryData.cppBinaryData.hJuceHeader.hReadMe.txtinclude_juce_build_tools.cppinclude_juce_core.cppinclude_juce_core.mminclude_juce_cryptography.cppinclude_juce_cryptography.mminclude_juce_data_structures.cppinclude_juce_data_structures.mminclude_juce_events.cppinclude_juce_events.mminclude_juce_graphics.cppinclude_juce_graphics.mminclude_juce_gui_basics.cppinclude_juce_gui_basics.mminclude_juce_gui_extra.cppinclude_juce_gui_extra.mm
Projucer.jucerSource
Application
StartPage
jucer_ContentComponents.hjucer_NewProjectTemplates.hjucer_NewProjectWizard.cppjucer_NewProjectWizard.hjucer_StartPageComponent.cppjucer_StartPageComponent.hjucer_StartPageTreeHolder.h
UserAccount
Windows
jucer_AboutWindowComponent.hjucer_EditorColourSchemeWindowComponent.hjucer_FloatingToolWindow.hjucer_GlobalPathsWindowComponent.hjucer_PIPCreatorWindowComponent.hjucer_SVGPathDataWindowComponent.hjucer_TranslationToolWindowComponent.hjucer_UTF8WindowComponent.h
jucer_Application.cppjucer_Application.hjucer_AutoUpdater.cppjucer_AutoUpdater.hjucer_CommandIDs.hjucer_CommandLine.cppjucer_CommandLine.hjucer_CommonHeaders.hjucer_Headers.hjucer_Main.cppjucer_MainWindow.cppjucer_MainWindow.hBinaryData
Icons
background_logo.svgexport_android.svgexport_clion.svgexport_codeBlocks.svgexport_linux.svgexport_visualStudio.svgexport_xcode.svggpl_logo.svgjuce_icon.pngwizard_AnimatedApp.svgwizard_AudioApp.svgwizard_AudioPlugin.svgwizard_ConsoleApp.svgwizard_DLL.svgwizard_GUI.svgwizard_Highlight.svgwizard_OpenGL.svgwizard_Openfile.svgwizard_StaticLibrary.svg
Templates
jucer_AnimatedComponentSimpleTemplate.hjucer_AnimatedComponentTemplate.cppjucer_AnimatedComponentTemplate.hjucer_AudioComponentSimpleTemplate.hjucer_AudioComponentTemplate.cppjucer_AudioComponentTemplate.hjucer_AudioPluginEditorTemplate.cppjucer_AudioPluginEditorTemplate.hjucer_AudioPluginFilterTemplate.cppjucer_AudioPluginFilterTemplate.hjucer_ComponentTemplate.cppjucer_ComponentTemplate.hjucer_ContentCompSimpleTemplate.hjucer_ContentCompTemplate.cppjucer_ContentCompTemplate.hjucer_InlineComponentTemplate.hjucer_MainConsoleAppTemplate.cppjucer_MainTemplate_NoWindow.cppjucer_MainTemplate_Window.cppjucer_NewComponentTemplate.cppjucer_NewComponentTemplate.hjucer_NewCppFileTemplate.cppjucer_NewCppFileTemplate.hjucer_NewInlineComponentTemplate.hjucer_OpenGLComponentSimpleTemplate.hjucer_OpenGLComponentTemplate.cppjucer_OpenGLComponentTemplate.hjucer_PIPAudioProcessorTemplate.hjucer_PIPTemplate.h
colourscheme_dark.xmlcolourscheme_light.xmlgradle
CodeEditor
jucer_DocumentEditorComponent.cppjucer_DocumentEditorComponent.hjucer_ItemPreviewComponent.hjucer_OpenDocumentManager.cppjucer_OpenDocumentManager.hjucer_SourceCodeEditor.cppjucer_SourceCodeEditor.h
ComponentEditor
Components
jucer_ButtonHandler.hjucer_ComboBoxHandler.hjucer_ComponentNameProperty.hjucer_ComponentTypeHandler.cppjucer_ComponentTypeHandler.hjucer_ComponentUndoableAction.hjucer_GenericComponentHandler.hjucer_GroupComponentHandler.hjucer_HyperlinkButtonHandler.hjucer_ImageButtonHandler.hjucer_JucerComponentHandler.hjucer_LabelHandler.hjucer_SliderHandler.hjucer_TabbedComponentHandler.hjucer_TextButtonHandler.hjucer_TextEditorHandler.hjucer_ToggleButtonHandler.hjucer_TreeViewHandler.hjucer_ViewportHandler.h
Documents
PaintElements
jucer_ColouredElement.cppjucer_ColouredElement.hjucer_ElementSiblingComponent.hjucer_FillType.hjucer_GradientPointComponent.hjucer_ImageResourceProperty.hjucer_PaintElement.cppjucer_PaintElement.hjucer_PaintElementEllipse.hjucer_PaintElementGroup.cppjucer_PaintElementGroup.hjucer_PaintElementImage.cppjucer_PaintElementImage.hjucer_PaintElementPath.cppjucer_PaintElementPath.hjucer_PaintElementRectangle.hjucer_PaintElementRoundedRectangle.hjucer_PaintElementText.hjucer_PaintElementUndoableAction.hjucer_PointComponent.hjucer_StrokeType.h
Properties
jucer_ColourPropertyComponent.hjucer_ComponentBooleanProperty.hjucer_ComponentChoiceProperty.hjucer_ComponentColourProperty.hjucer_ComponentTextProperty.hjucer_FilePropertyComponent.hjucer_FontPropertyComponent.hjucer_JustificationProperty.hjucer_PositionPropertyBase.h
UI
jucer_ComponentLayoutEditor.cppjucer_ComponentLayoutEditor.hjucer_ComponentLayoutPanel.hjucer_ComponentOverlayComponent.cppjucer_ComponentOverlayComponent.hjucer_EditingPanelBase.cppjucer_EditingPanelBase.hjucer_JucerCommandIDs.hjucer_JucerDocumentEditor.cppjucer_JucerDocumentEditor.hjucer_PaintRoutineEditor.cppjucer_PaintRoutineEditor.hjucer_PaintRoutinePanel.cppjucer_PaintRoutinePanel.hjucer_RelativePositionedRectangle.hjucer_ResourceEditorPanel.cppjucer_ResourceEditorPanel.hjucer_SnapGridPainter.hjucer_TestComponent.cppjucer_TestComponent.h
jucer_BinaryResources.cppjucer_BinaryResources.hjucer_ComponentLayout.cppjucer_ComponentLayout.hjucer_GeneratedCode.cppjucer_GeneratedCode.hjucer_JucerDocument.cppjucer_JucerDocument.hjucer_ObjectTypes.cppjucer_ObjectTypes.hjucer_PaintRoutine.cppjucer_PaintRoutine.hjucer_UtilityFunctions.hLicenses
LiveBuildEngine
Project
Modules
UI
Sidebar
jucer_ExporterTreeItems.hjucer_FileTreeItems.hjucer_ModuleTreeItems.hjucer_ProjectTreeItemBase.hjucer_Sidebar.hjucer_TreeItemTypes.h
jucer_ContentViewComponent.hjucer_ContentViewComponents.hjucer_FileGroupInformationComponent.hjucer_HeaderComponent.cppjucer_HeaderComponent.hjucer_ModulesInformationComponent.hjucer_ProjectContentComponent.cppjucer_ProjectContentComponent.hjucer_ProjectMessagesComponent.hjucer_UserAvatarComponent.hProjectSaving
jucer_ProjectExport_Android.hjucer_ProjectExport_CLion.hjucer_ProjectExport_CodeBlocks.hjucer_ProjectExport_MSVC.hjucer_ProjectExport_Make.hjucer_ProjectExport_Xcode.hjucer_ProjectExporter.cppjucer_ProjectExporter.hjucer_ProjectSaver.cppjucer_ProjectSaver.hjucer_ResourceFile.cppjucer_ResourceFile.hjucer_XcodeProjectParser.h
Settings
jucer_AppearanceSettings.cppjucer_AppearanceSettings.hjucer_StoredSettings.cppjucer_StoredSettings.h
Utility
Helpers
jucer_CodeHelpers.cppjucer_CodeHelpers.hjucer_Colours.hjucer_FileHelpers.cppjucer_FileHelpers.hjucer_MiscUtilities.cppjucer_MiscUtilities.hjucer_NewFileWizard.cppjucer_NewFileWizard.hjucer_PresetIDs.hjucer_TranslationHelpers.hjucer_ValueSourceHelpers.hjucer_ValueWithDefaultWrapper.hjucer_VersionInfo.cppjucer_VersionInfo.h
PIPs
UI
PropertyComponents
jucer_ColourPropertyComponent.hjucer_FilePathPropertyComponent.hjucer_LabelPropertyComponent.hjucer_PropertyComponentsWithEnablement.h
jucer_IconButton.hjucer_Icons.cppjucer_Icons.hjucer_JucerTreeViewBase.cppjucer_JucerTreeViewBase.hjucer_ProjucerLookAndFeel.cppjucer_ProjucerLookAndFeel.hjucer_SlidingPanelComponent.cppjucer_SlidingPanelComponent.hUnitTestRunner
Builds
LinuxMakefile
MacOSX
VisualStudio2017
UnitTestRunner.slnUnitTestRunner_ConsoleApp.vcxprojUnitTestRunner_ConsoleApp.vcxproj.filtersresources.rc
VisualStudio2019
JuceLibraryCode
JuceHeader.hReadMe.txtinclude_juce_analytics.cppinclude_juce_audio_basics.cppinclude_juce_audio_basics.mminclude_juce_audio_devices.cppinclude_juce_audio_devices.mminclude_juce_audio_formats.cppinclude_juce_audio_formats.mminclude_juce_audio_processors.cppinclude_juce_audio_processors.mminclude_juce_audio_utils.cppinclude_juce_audio_utils.mminclude_juce_core.cppinclude_juce_core.mminclude_juce_cryptography.cppinclude_juce_cryptography.mminclude_juce_data_structures.cppinclude_juce_data_structures.mminclude_juce_dsp.cppinclude_juce_dsp.mminclude_juce_events.cppinclude_juce_events.mminclude_juce_graphics.cppinclude_juce_graphics.mminclude_juce_gui_basics.cppinclude_juce_gui_basics.mminclude_juce_gui_extra.cppinclude_juce_gui_extra.mminclude_juce_opengl.cppinclude_juce_opengl.mminclude_juce_osc.cppinclude_juce_product_unlocking.cppinclude_juce_product_unlocking.mminclude_juce_video.cppinclude_juce_video.mm
Source
UnitTestRunner.jucerWindowsDLL
Builds
VisualStudio2019
JuceLibraryCode
JuceHeader.hReadMe.txtinclude_juce_audio_basics.cppinclude_juce_audio_basics.mminclude_juce_audio_devices.cppinclude_juce_audio_devices.mminclude_juce_audio_formats.cppinclude_juce_audio_formats.mminclude_juce_audio_processors.cppinclude_juce_audio_processors.mminclude_juce_audio_utils.cppinclude_juce_audio_utils.mminclude_juce_core.cppinclude_juce_core.mminclude_juce_cryptography.cppinclude_juce_cryptography.mminclude_juce_data_structures.cppinclude_juce_data_structures.mminclude_juce_events.cppinclude_juce_events.mminclude_juce_graphics.cppinclude_juce_graphics.mminclude_juce_gui_basics.cppinclude_juce_gui_basics.mminclude_juce_gui_extra.cppinclude_juce_gui_extra.mminclude_juce_opengl.cppinclude_juce_opengl.mminclude_juce_video.cppinclude_juce_video.mm
WindowsDLL.jucermodules
CMakeLists.txtjuce_android_Audio.cppjuce_android_HighPerformanceAudioHelpers.hjuce_android_Midi.cppjuce_android_Oboe.cppjuce_android_OpenSL.cppjuce_ios_Audio.cppjuce_ios_Audio.hjuce_linux_ALSA.cppjuce_linux_Bela.cppjuce_linux_JackAudio.cppjuce_linux_Midi.cppjuce_mac_CoreAudio.cppjuce_mac_CoreMidi.mmjuce_win32_ASIO.cppjuce_win32_DirectSound.cppjuce_win32_Midi.cppjuce_win32_WASAPI.cppAUResources.rjuce_audio_processors.cppjuce_audio_processors.hjuce_audio_processors.mmjuce_MultiTouchMapper.hjuce_ScopedDPIAwarenessDisabler.hjuce_android_ContentSharer.cppjuce_android_FileChooser.cppjuce_android_Windowing.cppjuce_common_MimeTypes.cppjuce_ios_ContentSharer.cppjuce_ios_FileChooser.mmjuce_ios_UIViewComponentPeer.mmjuce_ios_Windowing.mmjuce_linux_FileChooser.cppjuce_linux_Windowing.cppjuce_mac_FileChooser.mmjuce_mac_MainMenu.mmjuce_mac_MouseCursor.mmjuce_mac_NSViewComponentPeer.mmjuce_mac_Windowing.mmjuce_win32_DragAndDrop.cppjuce_win32_FileChooser.cppjuce_win32_ScopedThreadDPIAwarenessSetter.hjuce_win32_Windowing.cppjuce_OpenGLExtensions.hjuce_OpenGL_android.hjuce_OpenGL_ios.hjuce_OpenGL_linux_X11.hjuce_OpenGL_osx.hjuce_OpenGL_win32.h
juce_analytics
juce_audio_basics
audio_play_head
buffers
juce_AudioChannelSet.cppjuce_AudioChannelSet.hjuce_AudioDataConverters.cppjuce_AudioDataConverters.hjuce_AudioProcessLoadMeasurer.cppjuce_AudioProcessLoadMeasurer.hjuce_AudioSampleBuffer.hjuce_FloatVectorOperations.cppjuce_FloatVectorOperations.h
juce_audio_basics.cppjuce_audio_basics.hjuce_audio_basics.mmmidi
juce_MidiBuffer.cppjuce_MidiBuffer.hjuce_MidiDataConcatenator.hjuce_MidiFile.cppjuce_MidiFile.hjuce_MidiKeyboardState.cppjuce_MidiKeyboardState.hjuce_MidiMessage.cppjuce_MidiMessage.hjuce_MidiMessageSequence.cppjuce_MidiMessageSequence.hjuce_MidiRPN.cppjuce_MidiRPN.h
ump
juce_UMP.hjuce_UMPConversion.hjuce_UMPConverters.hjuce_UMPDispatcher.hjuce_UMPFactory.hjuce_UMPIterator.hjuce_UMPMidi1ToBytestreamTranslator.hjuce_UMPMidi1ToMidi2DefaultTranslator.cppjuce_UMPMidi1ToMidi2DefaultTranslator.hjuce_UMPProtocols.hjuce_UMPReceiver.hjuce_UMPSysEx7.cppjuce_UMPSysEx7.hjuce_UMPTests.cppjuce_UMPUtils.cppjuce_UMPUtils.hjuce_UMPView.cppjuce_UMPView.hjuce_UMPacket.hjuce_UMPackets.h
mpe
juce_MPEInstrument.cppjuce_MPEInstrument.cpp.origjuce_MPEInstrument.hjuce_MPEInstrument.h.origjuce_MPEMessages.cppjuce_MPEMessages.hjuce_MPENote.cppjuce_MPENote.hjuce_MPESynthesiser.cppjuce_MPESynthesiser.hjuce_MPESynthesiserBase.cppjuce_MPESynthesiserBase.hjuce_MPESynthesiserVoice.cppjuce_MPESynthesiserVoice.hjuce_MPEUtils.cppjuce_MPEUtils.hjuce_MPEValue.cppjuce_MPEValue.hjuce_MPEZoneLayout.cppjuce_MPEZoneLayout.h
native
sources
juce_AudioSource.hjuce_BufferingAudioSource.cppjuce_BufferingAudioSource.hjuce_ChannelRemappingAudioSource.cppjuce_ChannelRemappingAudioSource.hjuce_IIRFilterAudioSource.cppjuce_IIRFilterAudioSource.hjuce_MemoryAudioSource.cppjuce_MemoryAudioSource.hjuce_MixerAudioSource.cppjuce_MixerAudioSource.hjuce_PositionableAudioSource.hjuce_ResamplingAudioSource.cppjuce_ResamplingAudioSource.hjuce_ReverbAudioSource.cppjuce_ReverbAudioSource.hjuce_ToneGeneratorAudioSource.cppjuce_ToneGeneratorAudioSource.h
synthesisers
utilities
juce_audio_devices
audio_io
juce_AudioDeviceManager.cppjuce_AudioDeviceManager.hjuce_AudioIODevice.cppjuce_AudioIODevice.hjuce_AudioIODeviceType.cppjuce_AudioIODeviceType.hjuce_SystemAudioVolume.h
juce_audio_devices.cppjuce_audio_devices.hjuce_audio_devices.mmmidi_io
native
java
app
com
rmsl
oboe
CMakeLists.txtLICENSEREADME.md
include
oboe
src
aaudio
common
AudioClock.hAudioSourceCaller.cppAudioSourceCaller.hAudioStream.cppAudioStreamBuilder.cppDataConversionFlowGraph.cppDataConversionFlowGraph.hFilterAudioStream.cppFilterAudioStream.hFixedBlockAdapter.cppFixedBlockAdapter.hFixedBlockReader.cppFixedBlockReader.hFixedBlockWriter.cppFixedBlockWriter.hLatencyTuner.cppMonotonicCounter.hOboeDebug.hQuirksManager.cppQuirksManager.hSourceFloatCaller.cppSourceFloatCaller.hSourceI16Caller.cppSourceI16Caller.hSourceI24Caller.cppSourceI24Caller.hSourceI32Caller.cppSourceI32Caller.hStabilizedCallback.cppTrace.cppTrace.hUtilities.cppVersion.cpp
fifo
FifoBuffer.cppFifoBuffer.hFifoController.cppFifoController.hFifoControllerBase.cppFifoControllerBase.hFifoControllerIndirect.cppFifoControllerIndirect.h
flowgraph
ChannelCountConverter.cppChannelCountConverter.hClipToRange.cppClipToRange.hFlowGraphNode.cppFlowGraphNode.hFlowgraphUtilities.hManyToMultiConverter.cppManyToMultiConverter.hMonoToMultiConverter.cppMonoToMultiConverter.hMultiToMonoConverter.cppMultiToMonoConverter.hRampLinear.cppRampLinear.hSampleRateConverter.cppSampleRateConverter.hSinkFloat.cppSinkFloat.hSinkI16.cppSinkI16.hSinkI24.cppSinkI24.hSinkI32.cppSinkI32.hSourceFloat.cppSourceFloat.hSourceI16.cppSourceI16.hSourceI24.cppSourceI24.hSourceI32.cppSourceI32.h
resampler
HyperbolicCosineWindow.hIntegerRatio.cppIntegerRatio.hKaiserWindow.hLinearResampler.cppLinearResampler.hMultiChannelResampler.cppMultiChannelResampler.hPolyphaseResampler.cppPolyphaseResampler.hPolyphaseResamplerMono.cppPolyphaseResamplerMono.hPolyphaseResamplerStereo.cppPolyphaseResamplerStereo.hSincResampler.cppSincResampler.hSincResamplerStereo.cppSincResamplerStereo.h
opensles
AudioInputStreamOpenSLES.cppAudioInputStreamOpenSLES.hAudioOutputStreamOpenSLES.cppAudioOutputStreamOpenSLES.hAudioStreamBuffered.cppAudioStreamBuffered.hAudioStreamOpenSLES.cppAudioStreamOpenSLES.hEngineOpenSLES.cppEngineOpenSLES.hOpenSLESUtilities.cppOpenSLESUtilities.hOutputMixerOpenSLES.cppOutputMixerOpenSLES.h
sources
juce_audio_formats
codecs
flac
Flac Licence.txtall.halloc.hassert.hcallback.hcompat.hendswap.hexport.hformat.h
juce_AiffAudioFormat.cppjuce_AiffAudioFormat.hjuce_CoreAudioFormat.cppjuce_CoreAudioFormat.hjuce_FlacAudioFormat.cppjuce_FlacAudioFormat.hjuce_LAMEEncoderAudioFormat.cppjuce_LAMEEncoderAudioFormat.hjuce_MP3AudioFormat.cppjuce_MP3AudioFormat.hjuce_OggVorbisAudioFormat.cppjuce_OggVorbisAudioFormat.hjuce_WavAudioFormat.cppjuce_WavAudioFormat.hjuce_WindowsMediaAudioFormat.cppjuce_WindowsMediaAudioFormat.hlibFLAC
bitmath.cbitreader.cbitwriter.ccpu.ccrc.cfixed.cfloat.cformat.clpc_flac.cmd5.cmemory.cstream_decoder.cstream_encoder.cstream_encoder_framing.cwindow_flac.c
metadata.hordinals.hstream_decoder.hstream_encoder.hwin_utf8_io.hinclude
private
all.hbitmath.hbitreader.hbitwriter.hcpu.hcrc.hfixed.hfloat.hformat.hlpc.hmd5.hmemory.hmetadata.hstream_encoder.hstream_encoder_framing.hwindow.h
protected
oggvorbis
Ogg Vorbis Licence.txtbitwise.ccodec.hconfig_types.hcrctable.hframing.c
libvorbis-1.3.7
AUTHORSCHANGESCOPYINGREADME.md
ogg.hos_types.hvorbisenc.hvorbisfile.hlib
analysis.cbackends.hbitrate.cbitrate.hblock.c
books
codebook.ccodebook.hcodec_internal.henvelope.cenvelope.hfloor0.cfloor1.chighlevel.hinfo.clookup.clookup.hlookup_data.hlpc.clpc.hlsp.clsp.hmapping0.cmasking.hmdct.cmdct.hmisc.cmisc.hmodes
floor_all.hpsych_11.hpsych_16.hpsych_44.hpsych_8.hresidue_16.hresidue_44.hresidue_44p51.hresidue_44u.hresidue_8.hsetup_11.hsetup_16.hsetup_22.hsetup_32.hsetup_44.hsetup_44p51.hsetup_44u.hsetup_8.hsetup_X.h
os.hpsy.cpsy.hregistry.cregistry.hres0.cscales.hsharedbook.csmallft.csmallft.hsynthesis.cvorbisenc.cvorbisfile.cwindow.cwindow.hformat
juce_AudioFormat.cppjuce_AudioFormat.hjuce_AudioFormatManager.cppjuce_AudioFormatManager.hjuce_AudioFormatReader.cppjuce_AudioFormatReader.hjuce_AudioFormatReaderSource.cppjuce_AudioFormatReaderSource.hjuce_AudioFormatWriter.cppjuce_AudioFormatWriter.hjuce_AudioSubsectionReader.cppjuce_AudioSubsectionReader.hjuce_BufferingAudioFormatReader.cppjuce_BufferingAudioFormatReader.hjuce_MemoryMappedAudioFormatReader.h
juce_audio_formats.cppjuce_audio_formats.hjuce_audio_formats.mmsampler
juce_audio_plugin_client
AAX
AU
CoreAudioUtilityClasses
AUBase.cppAUBase.hAUBaseHelper.hAUBuffer.cppAUBuffer.hAUCarbonViewBase.cppAUCarbonViewBase.hAUCarbonViewControl.cppAUCarbonViewControl.hAUCarbonViewDispatch.cppAUDispatch.cppAUDispatch.hAUInputElement.cppAUInputElement.hAUInputFormatConverter.hAUMIDIBase.cppAUMIDIBase.hAUOutputBase.cppAUOutputBase.hAUOutputElement.cppAUOutputElement.hAUPlugInDispatch.cppAUPlugInDispatch.hAUScopeElement.cppAUScopeElement.hAUSilentTimeout.hAUTimestampGenerator.hAUViewLocalizedStringKeys.hCAAUParameter.cppCAAUParameter.hCAAtomic.hCAAtomicStack.hCAAudioChannelLayout.cppCAAudioChannelLayout.hCAAutoDisposer.hCADebugMacros.hCADebugPrintf.hCAException.hCAHostTimeBase.hCAMath.hCAMutex.cppCAMutex.hCAReferenceCounted.hCAStreamBasicDescription.cppCAStreamBasicDescription.hCAThreadSafeList.hCAVectorUnit.cppCAVectorUnit.hCAVectorUnitTypes.hCAXException.hCarbonEventHandler.cppCarbonEventHandler.hComponentBase.cppComponentBase.hMusicDeviceBase.cppMusicDeviceBase.h
juce_AU_Wrapper.mmjuce_AUv3_Wrapper.mmRTAS
juce_RTAS_DigiCode1.cppjuce_RTAS_DigiCode2.cppjuce_RTAS_DigiCode3.cppjuce_RTAS_DigiCode_Header.hjuce_RTAS_MacUtilities.mmjuce_RTAS_WinExports.defjuce_RTAS_WinResources.rsrjuce_RTAS_WinUtilities.cppjuce_RTAS_Wrapper.cpp
Standalone
Unity
VST
VST3
juce_audio_plugin_client.hjuce_audio_plugin_client_AAX.cppjuce_audio_plugin_client_AAX.mmjuce_audio_plugin_client_AU.rjuce_audio_plugin_client_AU_1.mmjuce_audio_plugin_client_AU_2.mmjuce_audio_plugin_client_AUv3.mmjuce_audio_plugin_client_RTAS.rjuce_audio_plugin_client_RTAS_1.cppjuce_audio_plugin_client_RTAS_2.cppjuce_audio_plugin_client_RTAS_3.cppjuce_audio_plugin_client_RTAS_4.cppjuce_audio_plugin_client_RTAS_utils.cppjuce_audio_plugin_client_RTAS_utils.mmjuce_audio_plugin_client_Standalone.cppjuce_audio_plugin_client_Unity.cppjuce_audio_plugin_client_VST2.cppjuce_audio_plugin_client_VST3.cppjuce_audio_plugin_client_VST_utils.mmjuce_audio_plugin_client_utils.cpputility
juce_audio_processors
format
juce_AudioPluginFormat.cppjuce_AudioPluginFormat.hjuce_AudioPluginFormatManager.cppjuce_AudioPluginFormatManager.h
format_types
VST3_SDK
JUCE_README.mdLICENSE.txtREADME.mdVST3_License_Agreement.pdfVST3_Usage_Guidelines.pdf
juce_AU_Shared.hjuce_AudioUnitPluginFormat.hjuce_AudioUnitPluginFormat.mmjuce_LADSPAPluginFormat.cppjuce_LADSPAPluginFormat.hjuce_LegacyAudioParameter.cppjuce_VST3Common.hjuce_VST3Headers.hjuce_VST3PluginFormat.cppjuce_VST3PluginFormat.hjuce_VSTCommon.hjuce_VSTMidiEventList.hjuce_VSTPluginFormat.cppjuce_VSTPluginFormat.hbase
LICENSE.txtREADME.md
source
baseiids.cppclassfactoryhelpers.hfbuffer.cppfbuffer.hfdebug.cppfdebug.hfobject.cppfobject.hfstreamer.cppfstreamer.hfstring.cppfstring.hupdatehandler.cppupdatehandler.h
thread
pluginterfaces
LICENSE.txtREADME.md
base
conststringtable.cppconststringtable.hcoreiids.cppfalignpop.hfalignpush.hfplatform.hfstrdefs.hftypes.hfunknown.cppfunknown.hfutils.hfvariant.hibstream.hicloneable.hipersistent.hipluginbase.histringresult.hiupdatehandler.hsmartpointer.htypesizecheck.hustring.cppustring.h
gui
vst
ivstattributes.hivstaudioprocessor.hivstautomationstate.hivstchannelcontextinfo.hivstcomponent.hivstcontextmenu.hivsteditcontroller.hivstevents.hivsthostapplication.hivstinterappaudio.hivstmessage.hivstmidicontrollers.hivstmidilearn.hivstnoteexpression.hivstparameterchanges.hivstparameterfunctionname.hivstphysicalui.hivstpluginterfacesupport.hivstplugview.hivstprefetchablesupport.hivstprocesscontext.hivstrepresentation.hivsttestplugprovider.hivstunits.hvstpshpack4.hvstspeaker.hvsttypes.h
public.sdk
processors
juce_AudioPluginInstance.cppjuce_AudioPluginInstance.hjuce_AudioProcessor.cppjuce_AudioProcessor.hjuce_AudioProcessorEditor.cppjuce_AudioProcessorEditor.hjuce_AudioProcessorEditorHostContext.hjuce_AudioProcessorGraph.cppjuce_AudioProcessorGraph.hjuce_AudioProcessorListener.hjuce_AudioProcessorParameter.hjuce_AudioProcessorParameterGroup.cppjuce_AudioProcessorParameterGroup.hjuce_GenericAudioProcessorEditor.cppjuce_GenericAudioProcessorEditor.hjuce_PluginDescription.cppjuce_PluginDescription.h
scanning
juce_KnownPluginList.cppjuce_KnownPluginList.hjuce_PluginDirectoryScanner.cppjuce_PluginDirectoryScanner.hjuce_PluginListComponent.cppjuce_PluginListComponent.h
utilities
juce_AudioParameterBool.cppjuce_AudioParameterBool.hjuce_AudioParameterChoice.cppjuce_AudioParameterChoice.hjuce_AudioParameterFloat.cppjuce_AudioParameterFloat.hjuce_AudioParameterInt.cppjuce_AudioParameterInt.hjuce_AudioProcessorParameterWithID.cppjuce_AudioProcessorParameterWithID.hjuce_AudioProcessorValueTreeState.cppjuce_AudioProcessorValueTreeState.hjuce_ExtensionsVisitor.hjuce_ParameterAttachments.cppjuce_ParameterAttachments.hjuce_PluginHostType.cppjuce_PluginHostType.hjuce_RangedAudioParameter.cppjuce_RangedAudioParameter.hjuce_VST3ClientExtensions.hjuce_VSTCallbackHandler.h
juce_audio_utils
audio_cd
gui
juce_AudioAppComponent.cppjuce_AudioAppComponent.hjuce_AudioDeviceSelectorComponent.cppjuce_AudioDeviceSelectorComponent.hjuce_AudioThumbnail.cppjuce_AudioThumbnail.hjuce_AudioThumbnailBase.hjuce_AudioThumbnailCache.cppjuce_AudioThumbnailCache.hjuce_AudioVisualiserComponent.cppjuce_AudioVisualiserComponent.hjuce_BluetoothMidiDevicePairingDialogue.hjuce_MidiKeyboardComponent.cppjuce_MidiKeyboardComponent.h
juce_audio_utils.cppjuce_audio_utils.hjuce_audio_utils.mmnative
juce_android_BluetoothMidiDevicePairingDialogue.cppjuce_ios_BluetoothMidiDevicePairingDialogue.mmjuce_linux_AudioCDReader.cppjuce_linux_BluetoothMidiDevicePairingDialogue.cppjuce_mac_AudioCDBurner.mmjuce_mac_AudioCDReader.mmjuce_mac_BluetoothMidiDevicePairingDialogue.mmjuce_win32_AudioCDBurner.cppjuce_win32_AudioCDReader.cppjuce_win_BluetoothMidiDevicePairingDialogue.cpp
players
juce_box2d
box2d
Box2D.hREADME.txt
juce_box2d.cppjuce_box2d.hCollision
Shapes
b2ChainShape.cppb2ChainShape.hb2CircleShape.cppb2CircleShape.hb2EdgeShape.cppb2EdgeShape.hb2PolygonShape.cppb2PolygonShape.hb2Shape.h
b2BroadPhase.cppb2BroadPhase.hb2CollideCircle.cppb2CollideEdge.cppb2CollidePolygon.cppb2Collision.cppb2Collision.hb2Distance.cppb2Distance.hb2DynamicTree.cppb2DynamicTree.hb2TimeOfImpact.cppb2TimeOfImpact.hCommon
b2BlockAllocator.cppb2BlockAllocator.hb2Draw.cppb2Draw.hb2GrowableStack.hb2Math.cppb2Math.hb2Settings.cppb2Settings.hb2StackAllocator.cppb2StackAllocator.hb2Timer.cppb2Timer.h
Dynamics
Contacts
b2ChainAndCircleContact.cppb2ChainAndCircleContact.hb2ChainAndPolygonContact.cppb2ChainAndPolygonContact.hb2CircleContact.cppb2CircleContact.hb2Contact.cppb2Contact.hb2ContactSolver.cppb2ContactSolver.hb2EdgeAndCircleContact.cppb2EdgeAndCircleContact.hb2EdgeAndPolygonContact.cppb2EdgeAndPolygonContact.hb2PolygonAndCircleContact.cppb2PolygonAndCircleContact.hb2PolygonContact.cppb2PolygonContact.h
Joints
b2DistanceJoint.cppb2DistanceJoint.hb2FrictionJoint.cppb2FrictionJoint.hb2GearJoint.cppb2GearJoint.hb2Joint.cppb2Joint.hb2MouseJoint.cppb2MouseJoint.hb2PrismaticJoint.cppb2PrismaticJoint.hb2PulleyJoint.cppb2PulleyJoint.hb2RevoluteJoint.cppb2RevoluteJoint.hb2RopeJoint.cppb2RopeJoint.hb2WeldJoint.cppb2WeldJoint.hb2WheelJoint.cppb2WheelJoint.h
b2Body.cppb2Body.hb2ContactManager.cppb2ContactManager.hb2Fixture.cppb2Fixture.hb2Island.cppb2Island.hb2TimeStep.hb2World.cppb2World.hb2WorldCallbacks.cppb2WorldCallbacks.hRope
utils
juce_core
containers
juce_AbstractFifo.cppjuce_AbstractFifo.hjuce_Array.hjuce_ArrayAllocationBase.hjuce_ArrayBase.cppjuce_ArrayBase.hjuce_DynamicObject.cppjuce_DynamicObject.hjuce_ElementComparator.hjuce_HashMap.hjuce_HashMap_test.cppjuce_LinkedListPointer.hjuce_ListenerList.hjuce_NamedValueSet.cppjuce_NamedValueSet.hjuce_OwnedArray.cppjuce_OwnedArray.hjuce_PropertySet.cppjuce_PropertySet.hjuce_ReferenceCountedArray.cppjuce_ReferenceCountedArray.hjuce_ScopedValueSetter.hjuce_SingleThreadedAbstractFifo.hjuce_SortedSet.hjuce_SparseSet.cppjuce_SparseSet.hjuce_Variant.cppjuce_Variant.h
files
juce_DirectoryIterator.cppjuce_DirectoryIterator.hjuce_File.cppjuce_File.hjuce_FileFilter.cppjuce_FileFilter.hjuce_FileInputStream.cppjuce_FileInputStream.hjuce_FileOutputStream.cppjuce_FileOutputStream.hjuce_FileSearchPath.cppjuce_FileSearchPath.hjuce_MemoryMappedFile.hjuce_RangedDirectoryIterator.cppjuce_RangedDirectoryIterator.hjuce_TemporaryFile.cppjuce_TemporaryFile.hjuce_WildcardFileFilter.cppjuce_WildcardFileFilter.h
javascript
juce_core.cppjuce_core.hjuce_core.mmlogging
maths
juce_BigInteger.cppjuce_BigInteger.hjuce_Expression.cppjuce_Expression.hjuce_MathsFunctions.hjuce_NormalisableRange.hjuce_Random.cppjuce_Random.hjuce_Range.hjuce_StatisticsAccumulator.h
memory
juce_AllocationHooks.cppjuce_AllocationHooks.hjuce_Atomic.hjuce_ByteOrder.hjuce_ContainerDeletePolicy.hjuce_HeapBlock.hjuce_HeavyweightLeakedObjectDetector.hjuce_LeakedObjectDetector.hjuce_Memory.hjuce_MemoryBlock.cppjuce_MemoryBlock.hjuce_OptionalScopedPointer.hjuce_ReferenceCountedObject.hjuce_Reservoir.hjuce_ScopedPointer.hjuce_SharedResourcePointer.hjuce_Singleton.hjuce_WeakReference.h
misc
juce_ConsoleApplication.cppjuce_ConsoleApplication.hjuce_Functional.hjuce_Result.cppjuce_Result.hjuce_RuntimePermissions.cppjuce_RuntimePermissions.hjuce_Uuid.cppjuce_Uuid.hjuce_WindowsRegistry.h
native
java
javacore
juce_BasicNativeHeaders.hjuce_android_Files.cppjuce_android_JNIHelpers.cppjuce_android_JNIHelpers.hjuce_android_Misc.cppjuce_android_Network.cppjuce_android_RuntimePermissions.cppjuce_android_SystemStats.cppjuce_android_Threads.cppjuce_curl_Network.cppjuce_intel_SharedCode.hjuce_linux_CommonFile.cppjuce_linux_Files.cppjuce_linux_Network.cppjuce_linux_SystemStats.cppjuce_linux_Threads.cppjuce_mac_CFHelpers.hjuce_mac_Files.mmjuce_mac_Network.mmjuce_mac_ObjCHelpers.hjuce_mac_Strings.mmjuce_mac_SystemStats.mmjuce_mac_Threads.mmjuce_posix_IPAddress.hjuce_posix_NamedPipe.cppjuce_posix_SharedCode.hjuce_wasm_SystemStats.cppjuce_win32_ComSmartPtr.hjuce_win32_Files.cppjuce_win32_Network.cppjuce_win32_Registry.cppjuce_win32_SystemStats.cppjuce_win32_Threads.cppnetwork
juce_IPAddress.cppjuce_IPAddress.hjuce_MACAddress.cppjuce_MACAddress.hjuce_NamedPipe.cppjuce_NamedPipe.hjuce_Socket.cppjuce_Socket.hjuce_URL.cppjuce_URL.hjuce_WebInputStream.cppjuce_WebInputStream.h
streams
juce_BufferedInputStream.cppjuce_BufferedInputStream.hjuce_FileInputSource.cppjuce_FileInputSource.hjuce_InputSource.hjuce_InputStream.cppjuce_InputStream.hjuce_MemoryInputStream.cppjuce_MemoryInputStream.hjuce_MemoryOutputStream.cppjuce_MemoryOutputStream.hjuce_OutputStream.cppjuce_OutputStream.hjuce_SubregionStream.cppjuce_SubregionStream.hjuce_URLInputSource.cppjuce_URLInputSource.h
system
juce_CompilerSupport.hjuce_CompilerWarnings.hjuce_PlatformDefs.hjuce_StandardHeader.hjuce_SystemStats.cppjuce_SystemStats.hjuce_TargetPlatform.h
text
juce_Base64.cppjuce_Base64.hjuce_CharPointer_ASCII.hjuce_CharPointer_UTF16.hjuce_CharPointer_UTF32.hjuce_CharPointer_UTF8.hjuce_CharacterFunctions.cppjuce_CharacterFunctions.hjuce_Identifier.cppjuce_Identifier.hjuce_LocalisedStrings.cppjuce_LocalisedStrings.hjuce_NewLine.hjuce_String.cppjuce_String.hjuce_StringArray.cppjuce_StringArray.hjuce_StringPairArray.cppjuce_StringPairArray.hjuce_StringPool.cppjuce_StringPool.hjuce_StringRef.hjuce_TextDiff.cppjuce_TextDiff.h
threads
juce_ChildProcess.cppjuce_ChildProcess.hjuce_CriticalSection.hjuce_DynamicLibrary.hjuce_HighResolutionTimer.cppjuce_HighResolutionTimer.hjuce_InterProcessLock.hjuce_Process.hjuce_ReadWriteLock.cppjuce_ReadWriteLock.hjuce_ScopedLock.hjuce_ScopedReadLock.hjuce_ScopedWriteLock.hjuce_SpinLock.hjuce_Thread.cppjuce_Thread.hjuce_ThreadLocalValue.hjuce_ThreadPool.cppjuce_ThreadPool.hjuce_TimeSliceThread.cppjuce_TimeSliceThread.hjuce_WaitableEvent.cppjuce_WaitableEvent.h
time
juce_PerformanceCounter.cppjuce_PerformanceCounter.hjuce_RelativeTime.cppjuce_RelativeTime.hjuce_Time.cppjuce_Time.h
unit_tests
xml
zip
juce_cryptography
juce_data_structures
app_properties
juce_ApplicationProperties.cppjuce_ApplicationProperties.hjuce_PropertiesFile.cppjuce_PropertiesFile.h
juce_data_structures.cppjuce_data_structures.hjuce_data_structures.mmundomanager
values
juce_dsp
containers
juce_AudioBlock.hjuce_AudioBlock_test.cppjuce_FixedSizeFunction.hjuce_FixedSizeFunction_test.cppjuce_SIMDRegister.hjuce_SIMDRegister_Impl.hjuce_SIMDRegister_test.cpp
filter_design
frequency
juce_Convolution.cppjuce_Convolution.hjuce_Convolution_test.cppjuce_FFT.cppjuce_FFT.hjuce_FFT_test.cppjuce_Windowing.cppjuce_Windowing.h
juce_dsp.cppjuce_dsp.hjuce_dsp.mmmaths
juce_FastMathApproximations.hjuce_LogRampedValue.hjuce_LogRampedValue_test.cppjuce_LookupTable.cppjuce_LookupTable.hjuce_Matrix.cppjuce_Matrix.hjuce_Matrix_test.cppjuce_Phase.hjuce_Polynomial.hjuce_SpecialFunctions.cppjuce_SpecialFunctions.h
native
juce_avx_SIMDNativeOps.cppjuce_avx_SIMDNativeOps.hjuce_fallback_SIMDNativeOps.hjuce_neon_SIMDNativeOps.cppjuce_neon_SIMDNativeOps.hjuce_sse_SIMDNativeOps.cppjuce_sse_SIMDNativeOps.h
processors
juce_BallisticsFilter.cppjuce_BallisticsFilter.hjuce_DelayLine.cppjuce_DelayLine.hjuce_DryWetMixer.cppjuce_DryWetMixer.hjuce_FIRFilter.cppjuce_FIRFilter.hjuce_FIRFilter_test.cppjuce_FirstOrderTPTFilter.cppjuce_FirstOrderTPTFilter.hjuce_IIRFilter.cppjuce_IIRFilter.hjuce_IIRFilter_Impl.hjuce_LinkwitzRileyFilter.cppjuce_LinkwitzRileyFilter.hjuce_Oversampling.cppjuce_Oversampling.hjuce_Panner.cppjuce_Panner.hjuce_ProcessContext.hjuce_ProcessorChain.hjuce_ProcessorChain_test.cppjuce_ProcessorDuplicator.hjuce_ProcessorWrapper.hjuce_StateVariableFilter.hjuce_StateVariableTPTFilter.cppjuce_StateVariableTPTFilter.h
widgets
juce_events
broadcasters
juce_ActionBroadcaster.cppjuce_ActionBroadcaster.hjuce_ActionListener.hjuce_AsyncUpdater.cppjuce_AsyncUpdater.hjuce_ChangeBroadcaster.cppjuce_ChangeBroadcaster.hjuce_ChangeListener.h
interprocess
juce_ConnectedChildProcess.cppjuce_ConnectedChildProcess.hjuce_InterprocessConnection.cppjuce_InterprocessConnection.hjuce_InterprocessConnectionServer.cppjuce_InterprocessConnectionServer.hjuce_NetworkServiceDiscovery.cppjuce_NetworkServiceDiscovery.h
juce_events.cppjuce_events.hjuce_events.mmmessages
juce_ApplicationBase.cppjuce_ApplicationBase.hjuce_CallbackMessage.hjuce_DeletedAtShutdown.cppjuce_DeletedAtShutdown.hjuce_Initialisation.hjuce_Message.hjuce_MessageListener.cppjuce_MessageListener.hjuce_MessageManager.cppjuce_MessageManager.hjuce_MountedVolumeListChangeDetector.hjuce_NotificationType.h
native
juce_ScopedLowPowerModeDisabler.cppjuce_ScopedLowPowerModeDisabler.hjuce_android_Messaging.cppjuce_ios_MessageManager.mmjuce_linux_EventLoop.hjuce_linux_Messaging.cppjuce_mac_MessageManager.mmjuce_osx_MessageQueue.hjuce_win32_HiddenMessageWindow.hjuce_win32_Messaging.cppjuce_win32_WinRTWrapper.cppjuce_win32_WinRTWrapper.h
timers
juce_graphics
colour
juce_Colour.cppjuce_Colour.hjuce_ColourGradient.cppjuce_ColourGradient.hjuce_Colours.cppjuce_Colours.hjuce_FillType.cppjuce_FillType.hjuce_PixelFormats.h
contexts
juce_GraphicsContext.cppjuce_GraphicsContext.hjuce_LowLevelGraphicsContext.hjuce_LowLevelGraphicsPostScriptRenderer.cppjuce_LowLevelGraphicsPostScriptRenderer.hjuce_LowLevelGraphicsSoftwareRenderer.cppjuce_LowLevelGraphicsSoftwareRenderer.h
effects
juce_DropShadowEffect.cppjuce_DropShadowEffect.hjuce_GlowEffect.cppjuce_GlowEffect.hjuce_ImageEffectFilter.h
fonts
juce_AttributedString.cppjuce_AttributedString.hjuce_CustomTypeface.cppjuce_CustomTypeface.hjuce_Font.cppjuce_Font.hjuce_GlyphArrangement.cppjuce_GlyphArrangement.hjuce_TextLayout.cppjuce_TextLayout.hjuce_Typeface.cppjuce_Typeface.h
geometry
juce_AffineTransform.cppjuce_AffineTransform.hjuce_BorderSize.hjuce_EdgeTable.cppjuce_EdgeTable.hjuce_Line.hjuce_Parallelogram.hjuce_Path.cppjuce_Path.hjuce_PathIterator.cppjuce_PathIterator.hjuce_PathStrokeType.cppjuce_PathStrokeType.hjuce_Point.hjuce_Rectangle.hjuce_RectangleList.h
image_formats
jpglib
READMEcderror.hchanges to libjpeg for JUCE.txtjcapimin.cjcapistd.cjccoefct.cjccolor.cjcdctmgr.cjchuff.cjchuff.hjcinit.cjcmainct.cjcmarker.cjcmaster.cjcomapi.cjconfig.hjcparam.cjcphuff.cjcprepct.cjcsample.cjctrans.cjdapimin.cjdapistd.cjdatasrc.cjdcoefct.cjdcolor.cjdct.hjddctmgr.cjdhuff.cjdhuff.hjdinput.cjdmainct.cjdmarker.cjdmaster.cjdmerge.cjdphuff.cjdpostct.cjdsample.cjdtrans.cjerror.cjerror.hjfdctflt.cjfdctfst.cjfdctint.cjidctflt.cjidctfst.cjidctint.cjidctred.cjinclude.hjmemmgr.cjmemnobs.cjmemsys.hjmorecfg.hjpegint.hjpeglib.hjquant1.cjquant2.cjutils.cjversion.htransupp.ctransupp.h
juce_GIFLoader.cppjuce_JPEGLoader.cppjuce_PNGLoader.cpppnglib
images
juce_Image.cppjuce_Image.hjuce_ImageCache.cppjuce_ImageCache.hjuce_ImageConvolutionKernel.cppjuce_ImageConvolutionKernel.hjuce_ImageFileFormat.cppjuce_ImageFileFormat.h
juce_graphics.cppjuce_graphics.hjuce_graphics.mmnative
juce_RenderingHelpers.hjuce_android_Fonts.cppjuce_android_GraphicsContext.cppjuce_android_IconHelpers.cppjuce_freetype_Fonts.cppjuce_linux_Fonts.cppjuce_linux_IconHelpers.cppjuce_mac_CoreGraphicsContext.hjuce_mac_CoreGraphicsContext.mmjuce_mac_CoreGraphicsHelpers.hjuce_mac_Fonts.mmjuce_mac_IconHelpers.cppjuce_win32_Direct2DGraphicsContext.cppjuce_win32_Direct2DGraphicsContext.hjuce_win32_DirectWriteTypeLayout.cppjuce_win32_DirectWriteTypeface.cppjuce_win32_Fonts.cppjuce_win32_IconHelpers.cpp
placement
juce_gui_basics
accessibility
enums
interfaces
juce_AccessibilityCellInterface.hjuce_AccessibilityTableInterface.hjuce_AccessibilityTextInterface.hjuce_AccessibilityValueInterface.h
juce_AccessibilityHandler.cppjuce_AccessibilityHandler.hjuce_AccessibilityState.happlication
buttons
juce_ArrowButton.cppjuce_ArrowButton.hjuce_Button.cppjuce_Button.hjuce_DrawableButton.cppjuce_DrawableButton.hjuce_HyperlinkButton.cppjuce_HyperlinkButton.hjuce_ImageButton.cppjuce_ImageButton.hjuce_ShapeButton.cppjuce_ShapeButton.hjuce_TextButton.cppjuce_TextButton.hjuce_ToggleButton.cppjuce_ToggleButton.hjuce_ToolbarButton.cppjuce_ToolbarButton.h
commands
juce_ApplicationCommandID.hjuce_ApplicationCommandInfo.cppjuce_ApplicationCommandInfo.hjuce_ApplicationCommandManager.cppjuce_ApplicationCommandManager.hjuce_ApplicationCommandTarget.cppjuce_ApplicationCommandTarget.hjuce_KeyPressMappingSet.cppjuce_KeyPressMappingSet.h
components
juce_CachedComponentImage.hjuce_Component.cppjuce_Component.hjuce_ComponentListener.cppjuce_ComponentListener.hjuce_ComponentTraverser.hjuce_FocusTraverser.cppjuce_FocusTraverser.hjuce_ModalComponentManager.cppjuce_ModalComponentManager.h
desktop
drawables
juce_Drawable.cppjuce_Drawable.hjuce_DrawableComposite.cppjuce_DrawableComposite.hjuce_DrawableImage.cppjuce_DrawableImage.hjuce_DrawablePath.cppjuce_DrawablePath.hjuce_DrawableRectangle.cppjuce_DrawableRectangle.hjuce_DrawableShape.cppjuce_DrawableShape.hjuce_DrawableText.cppjuce_DrawableText.hjuce_SVGParser.cpp
filebrowser
juce_ContentSharer.cppjuce_ContentSharer.hjuce_DirectoryContentsDisplayComponent.cppjuce_DirectoryContentsDisplayComponent.hjuce_DirectoryContentsList.cppjuce_DirectoryContentsList.hjuce_FileBrowserComponent.cppjuce_FileBrowserComponent.hjuce_FileBrowserListener.hjuce_FileChooser.cppjuce_FileChooser.hjuce_FileChooserDialogBox.cppjuce_FileChooserDialogBox.hjuce_FileListComponent.cppjuce_FileListComponent.hjuce_FilePreviewComponent.hjuce_FileSearchPathListComponent.cppjuce_FileSearchPathListComponent.hjuce_FileTreeComponent.cppjuce_FileTreeComponent.hjuce_FilenameComponent.cppjuce_FilenameComponent.hjuce_ImagePreviewComponent.cppjuce_ImagePreviewComponent.h
juce_gui_basics.cppjuce_gui_basics.hjuce_gui_basics.mmkeyboard
juce_CaretComponent.cppjuce_CaretComponent.hjuce_KeyListener.cppjuce_KeyListener.hjuce_KeyPress.cppjuce_KeyPress.hjuce_KeyboardFocusTraverser.cppjuce_KeyboardFocusTraverser.hjuce_ModifierKeys.cppjuce_ModifierKeys.hjuce_SystemClipboard.hjuce_TextEditorKeyMapper.hjuce_TextInputTarget.h
layout
juce_AnimatedPosition.hjuce_AnimatedPositionBehaviours.hjuce_ComponentAnimator.cppjuce_ComponentAnimator.hjuce_ComponentBoundsConstrainer.cppjuce_ComponentBoundsConstrainer.hjuce_ComponentBuilder.cppjuce_ComponentBuilder.hjuce_ComponentMovementWatcher.cppjuce_ComponentMovementWatcher.hjuce_ConcertinaPanel.cppjuce_ConcertinaPanel.hjuce_FlexBox.cppjuce_FlexBox.hjuce_FlexItem.hjuce_Grid.cppjuce_Grid.hjuce_GridItem.cppjuce_GridItem.hjuce_GroupComponent.cppjuce_GroupComponent.hjuce_MultiDocumentPanel.cppjuce_MultiDocumentPanel.hjuce_ResizableBorderComponent.cppjuce_ResizableBorderComponent.hjuce_ResizableCornerComponent.cppjuce_ResizableCornerComponent.hjuce_ResizableEdgeComponent.cppjuce_ResizableEdgeComponent.hjuce_ScrollBar.cppjuce_ScrollBar.hjuce_SidePanel.cppjuce_SidePanel.hjuce_StretchableLayoutManager.cppjuce_StretchableLayoutManager.hjuce_StretchableLayoutResizerBar.cppjuce_StretchableLayoutResizerBar.hjuce_StretchableObjectResizer.cppjuce_StretchableObjectResizer.hjuce_TabbedButtonBar.cppjuce_TabbedButtonBar.hjuce_TabbedComponent.cppjuce_TabbedComponent.hjuce_Viewport.cppjuce_Viewport.h
lookandfeel
juce_LookAndFeel.cppjuce_LookAndFeel.hjuce_LookAndFeel_V1.cppjuce_LookAndFeel_V1.hjuce_LookAndFeel_V2.cppjuce_LookAndFeel_V2.hjuce_LookAndFeel_V3.cppjuce_LookAndFeel_V3.hjuce_LookAndFeel_V4.cppjuce_LookAndFeel_V4.h
menus
juce_BurgerMenuComponent.cppjuce_BurgerMenuComponent.hjuce_MenuBarComponent.cppjuce_MenuBarComponent.hjuce_MenuBarModel.cppjuce_MenuBarModel.hjuce_PopupMenu.cppjuce_PopupMenu.h
misc
juce_BubbleComponent.cppjuce_BubbleComponent.hjuce_DropShadower.cppjuce_DropShadower.hjuce_JUCESplashScreen.cppjuce_JUCESplashScreen.h
mouse
juce_ComponentDragger.cppjuce_ComponentDragger.hjuce_DragAndDropContainer.cppjuce_DragAndDropContainer.hjuce_DragAndDropTarget.hjuce_FileDragAndDropTarget.hjuce_LassoComponent.hjuce_MouseCursor.cppjuce_MouseCursor.hjuce_MouseEvent.cppjuce_MouseEvent.hjuce_MouseInactivityDetector.cppjuce_MouseInactivityDetector.hjuce_MouseInputSource.cppjuce_MouseInputSource.hjuce_MouseListener.cppjuce_MouseListener.hjuce_SelectedItemSet.hjuce_TextDragAndDropTarget.hjuce_TooltipClient.h
native
accessibility
juce_AccessibilityTextHelpers.hjuce_android_Accessibility.cppjuce_ios_Accessibility.mmjuce_mac_Accessibility.mmjuce_mac_AccessibilitySharedCode.mmjuce_win32_Accessibility.cppjuce_win32_AccessibilityElement.cppjuce_win32_AccessibilityElement.hjuce_win32_UIAExpandCollapseProvider.hjuce_win32_UIAGridItemProvider.hjuce_win32_UIAGridProvider.hjuce_win32_UIAHelpers.hjuce_win32_UIAInvokeProvider.hjuce_win32_UIAProviderBase.hjuce_win32_UIAProviders.hjuce_win32_UIARangeValueProvider.hjuce_win32_UIASelectionProvider.hjuce_win32_UIATextProvider.hjuce_win32_UIAToggleProvider.hjuce_win32_UIATransformProvider.hjuce_win32_UIAValueProvider.hjuce_win32_UIAWindowProvider.hjuce_win32_WindowsUIAWrapper.h
java
app
javaopt
app
x11
positioning
juce_MarkerList.cppjuce_MarkerList.hjuce_RelativeCoordinate.cppjuce_RelativeCoordinate.hjuce_RelativeCoordinatePositioner.cppjuce_RelativeCoordinatePositioner.hjuce_RelativeParallelogram.cppjuce_RelativeParallelogram.hjuce_RelativePoint.cppjuce_RelativePoint.hjuce_RelativePointPath.cppjuce_RelativePointPath.hjuce_RelativeRectangle.cppjuce_RelativeRectangle.h
properties
juce_BooleanPropertyComponent.cppjuce_BooleanPropertyComponent.hjuce_ButtonPropertyComponent.cppjuce_ButtonPropertyComponent.hjuce_ChoicePropertyComponent.cppjuce_ChoicePropertyComponent.hjuce_MultiChoicePropertyComponent.cppjuce_MultiChoicePropertyComponent.hjuce_PropertyComponent.cppjuce_PropertyComponent.hjuce_PropertyPanel.cppjuce_PropertyPanel.hjuce_SliderPropertyComponent.cppjuce_SliderPropertyComponent.hjuce_TextPropertyComponent.cppjuce_TextPropertyComponent.h
widgets
juce_ComboBox.cppjuce_ComboBox.hjuce_ImageComponent.cppjuce_ImageComponent.hjuce_Label.cppjuce_Label.hjuce_ListBox.cppjuce_ListBox.cpp.rejjuce_ListBox.hjuce_ProgressBar.cppjuce_ProgressBar.hjuce_Slider.cppjuce_Slider.hjuce_TableHeaderComponent.cppjuce_TableHeaderComponent.hjuce_TableListBox.cppjuce_TableListBox.hjuce_TextEditor.cppjuce_TextEditor.hjuce_Toolbar.cppjuce_Toolbar.hjuce_ToolbarItemComponent.cppjuce_ToolbarItemComponent.hjuce_ToolbarItemFactory.hjuce_ToolbarItemPalette.cppjuce_ToolbarItemPalette.hjuce_TreeView.cppjuce_TreeView.h
windows
juce_AlertWindow.cppjuce_AlertWindow.hjuce_CallOutBox.cppjuce_CallOutBox.hjuce_ComponentPeer.cppjuce_ComponentPeer.hjuce_DialogWindow.cppjuce_DialogWindow.hjuce_DocumentWindow.cppjuce_DocumentWindow.hjuce_MessageBoxOptions.hjuce_NativeMessageBox.hjuce_ResizableWindow.cppjuce_ResizableWindow.hjuce_ThreadWithProgressWindow.cppjuce_ThreadWithProgressWindow.hjuce_TooltipWindow.cppjuce_TooltipWindow.hjuce_TopLevelWindow.cppjuce_TopLevelWindow.h
juce_gui_extra
code_editor
juce_CPlusPlusCodeTokeniser.cppjuce_CPlusPlusCodeTokeniser.hjuce_CPlusPlusCodeTokeniserFunctions.hjuce_CodeDocument.cppjuce_CodeDocument.hjuce_CodeEditorComponent.cppjuce_CodeEditorComponent.hjuce_CodeTokeniser.hjuce_LuaCodeTokeniser.cppjuce_LuaCodeTokeniser.hjuce_XMLCodeTokeniser.cppjuce_XMLCodeTokeniser.h
documents
embedding
juce_ActiveXControlComponent.hjuce_AndroidViewComponent.hjuce_HWNDComponent.hjuce_NSViewComponent.hjuce_UIViewComponent.hjuce_XEmbedComponent.h
juce_gui_extra.cppjuce_gui_extra.hjuce_gui_extra.mmmisc
juce_AnimatedAppComponent.cppjuce_AnimatedAppComponent.hjuce_AppleRemote.hjuce_BubbleMessageComponent.cppjuce_BubbleMessageComponent.hjuce_ColourSelector.cppjuce_ColourSelector.hjuce_KeyMappingEditorComponent.cppjuce_KeyMappingEditorComponent.hjuce_LiveConstantEditor.cppjuce_LiveConstantEditor.hjuce_PreferencesPanel.cppjuce_PreferencesPanel.hjuce_PushNotifications.cppjuce_PushNotifications.hjuce_RecentlyOpenedFilesList.cppjuce_RecentlyOpenedFilesList.hjuce_SplashScreen.cppjuce_SplashScreen.hjuce_SystemTrayIconComponent.cppjuce_SystemTrayIconComponent.hjuce_WebBrowserComponent.h
native
java
app
com
javaopt
juce_AndroidViewComponent.cppjuce_android_PushNotifications.cppjuce_android_WebBrowserComponent.cppjuce_ios_PushNotifications.cppjuce_ios_UIViewComponent.mmjuce_linux_X11_SystemTrayIcon.cppjuce_linux_X11_WebBrowserComponent.cppjuce_linux_XEmbedComponent.cppjuce_mac_AppleRemote.mmjuce_mac_CarbonViewWrapperComponent.hjuce_mac_NSViewComponent.mmjuce_mac_PushNotifications.cppjuce_mac_SystemTrayIcon.cppjuce_mac_WebBrowserComponent.mmjuce_win32_ActiveXComponent.cppjuce_win32_HWNDComponent.cppjuce_win32_SystemTrayIcon.cppjuce_win32_WebBrowserComponent.cppjuce_opengl
geometry
juce_opengl.cppjuce_opengl.hjuce_opengl.mmnative
java
app
com
rmsl
opengl
juce_OpenGLContext.cppjuce_OpenGLContext.hjuce_OpenGLContext.h.rejjuce_OpenGLFrameBuffer.cppjuce_OpenGLFrameBuffer.hjuce_OpenGLGraphicsContext.cppjuce_OpenGLGraphicsContext.hjuce_OpenGLHelpers.cppjuce_OpenGLHelpers.hjuce_OpenGLImage.cppjuce_OpenGLImage.hjuce_OpenGLPixelFormat.cppjuce_OpenGLPixelFormat.hjuce_OpenGLRenderer.hjuce_OpenGLShaderProgram.cppjuce_OpenGLShaderProgram.hjuce_OpenGLTexture.cppjuce_OpenGLTexture.hjuce_gl.cppjuce_gl.hjuce_gles2.cppjuce_gles2.hjuce_khrplatform.hjuce_wgl.h
utils
juce_osc
juce_product_unlocking
in_app_purchases
juce_product_unlocking.cppjuce_product_unlocking.hjuce_product_unlocking.mmmarketplace
juce_KeyFileGeneration.hjuce_OnlineUnlockForm.cppjuce_OnlineUnlockForm.hjuce_OnlineUnlockStatus.cppjuce_OnlineUnlockStatus.hjuce_TracktionMarketplaceStatus.cppjuce_TracktionMarketplaceStatus.h
native
juce_video
207
deps/juce/modules/juce_gui_basics/layout/juce_AnimatedPosition.h
vendored
Normal file
207
deps/juce/modules/juce_gui_basics/layout/juce_AnimatedPosition.h
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Models a 1-dimensional position that can be dragged around by the user, and which
|
||||
will then continue moving with a customisable physics behaviour when released.
|
||||
|
||||
This is useful for things like scrollable views or objects that can be dragged and
|
||||
thrown around with the mouse/touch, and by writing your own behaviour class, you can
|
||||
customise the trajectory that it follows when released.
|
||||
|
||||
The class uses its own Timer to continuously change its value when a drag ends, and
|
||||
Listener objects can be registered to receive callbacks whenever the value changes.
|
||||
|
||||
The value is stored as a double, and can be used to represent whatever units you need.
|
||||
|
||||
The template parameter Behaviour must be a class that implements various methods to
|
||||
return the physics of the value's movement - you can use the classes provided for this
|
||||
in the AnimatedPositionBehaviours namespace, or write your own custom behaviour.
|
||||
|
||||
@see AnimatedPositionBehaviours::ContinuousWithMomentum,
|
||||
AnimatedPositionBehaviours::SnapToPageBoundaries
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
template <typename Behaviour>
|
||||
class AnimatedPosition : private Timer
|
||||
{
|
||||
public:
|
||||
AnimatedPosition()
|
||||
: range (-std::numeric_limits<double>::max(),
|
||||
std::numeric_limits<double>::max())
|
||||
{
|
||||
}
|
||||
|
||||
/** Sets a range within which the value will be constrained. */
|
||||
void setLimits (Range<double> newRange) noexcept
|
||||
{
|
||||
range = newRange;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Called to indicate that the object is now being controlled by a
|
||||
mouse-drag or similar operation.
|
||||
|
||||
After calling this method, you should make calls to the drag() method
|
||||
each time the mouse drags the position around, and always be sure to
|
||||
finish with a call to endDrag() when the mouse is released, which allows
|
||||
the position to continue moving freely according to the specified behaviour.
|
||||
*/
|
||||
void beginDrag()
|
||||
{
|
||||
grabbedPos = position;
|
||||
releaseVelocity = 0;
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
/** Called during a mouse-drag operation, to indicate that the mouse has moved.
|
||||
The delta is the difference between the position when beginDrag() was called
|
||||
and the new position that's required.
|
||||
*/
|
||||
void drag (double deltaFromStartOfDrag)
|
||||
{
|
||||
moveTo (grabbedPos + deltaFromStartOfDrag);
|
||||
}
|
||||
|
||||
/** Called after beginDrag() and drag() to indicate that the drag operation has
|
||||
now finished.
|
||||
*/
|
||||
void endDrag()
|
||||
{
|
||||
startTimerHz (60);
|
||||
}
|
||||
|
||||
/** Called outside of a drag operation to cause a nudge in the specified direction.
|
||||
This is intended for use by e.g. mouse-wheel events.
|
||||
*/
|
||||
void nudge (double deltaFromCurrentPosition)
|
||||
{
|
||||
startTimerHz (10);
|
||||
moveTo (position + deltaFromCurrentPosition);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current position. */
|
||||
double getPosition() const noexcept
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
/** Explicitly sets the position and stops any further movement.
|
||||
This will cause a synchronous call to any listeners if the position actually
|
||||
changes.
|
||||
*/
|
||||
void setPosition (double newPosition)
|
||||
{
|
||||
stopTimer();
|
||||
setPositionAndSendChange (newPosition);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Implement this class if you need to receive callbacks when the value of
|
||||
an AnimatedPosition changes.
|
||||
@see AnimatedPosition::addListener, AnimatedPosition::removeListener
|
||||
*/
|
||||
class Listener
|
||||
{
|
||||
public:
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called synchronously when an AnimatedPosition changes. */
|
||||
virtual void positionChanged (AnimatedPosition&, double newPosition) = 0;
|
||||
};
|
||||
|
||||
/** Adds a listener to be called when the value changes. */
|
||||
void addListener (Listener* listener) { listeners.add (listener); }
|
||||
|
||||
/** Removes a previously-registered listener. */
|
||||
void removeListener (Listener* listener) { listeners.remove (listener); }
|
||||
|
||||
//==============================================================================
|
||||
/** The behaviour object.
|
||||
This is public to let you tweak any parameters that it provides.
|
||||
*/
|
||||
Behaviour behaviour;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
double position = 0.0, grabbedPos = 0.0, releaseVelocity = 0.0;
|
||||
Range<double> range;
|
||||
Time lastUpdate, lastDrag;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
static double getSpeed (const Time last, double lastPos,
|
||||
const Time now, double newPos)
|
||||
{
|
||||
auto elapsedSecs = jmax (0.005, (now - last).inSeconds());
|
||||
auto v = (newPos - lastPos) / elapsedSecs;
|
||||
return std::abs (v) > 0.2 ? v : 0.0;
|
||||
}
|
||||
|
||||
void moveTo (double newPos)
|
||||
{
|
||||
auto now = Time::getCurrentTime();
|
||||
releaseVelocity = getSpeed (lastDrag, position, now, newPos);
|
||||
behaviour.releasedWithVelocity (newPos, releaseVelocity);
|
||||
lastDrag = now;
|
||||
|
||||
setPositionAndSendChange (newPos);
|
||||
}
|
||||
|
||||
void setPositionAndSendChange (double newPosition)
|
||||
{
|
||||
newPosition = range.clipValue (newPosition);
|
||||
|
||||
if (position != newPosition)
|
||||
{
|
||||
position = newPosition;
|
||||
listeners.call ([this, newPosition] (Listener& l) { l.positionChanged (*this, newPosition); });
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
auto now = Time::getCurrentTime();
|
||||
auto elapsed = jlimit (0.001, 0.020, (now - lastUpdate).inSeconds());
|
||||
lastUpdate = now;
|
||||
auto newPos = behaviour.getNextPosition (position, elapsed);
|
||||
|
||||
if (behaviour.isStopped (newPos))
|
||||
stopTimer();
|
||||
else
|
||||
startTimerHz (60);
|
||||
|
||||
setPositionAndSendChange (newPos);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimatedPosition)
|
||||
};
|
||||
|
||||
} // namespace juce
|
156
deps/juce/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h
vendored
Normal file
156
deps/juce/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** Contains classes for different types of physics behaviours - these classes
|
||||
are used as template parameters for the AnimatedPosition class.
|
||||
*/
|
||||
namespace AnimatedPositionBehaviours
|
||||
{
|
||||
/** A non-snapping behaviour that allows the content to be freely flicked in
|
||||
either direction, with momentum based on the velocity at which it was
|
||||
released, and variable friction to make it come to a halt.
|
||||
|
||||
This class is intended to be used as a template parameter to the
|
||||
AnimatedPosition class.
|
||||
|
||||
@see AnimatedPosition
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
struct ContinuousWithMomentum
|
||||
{
|
||||
ContinuousWithMomentum() = default;
|
||||
|
||||
/** Sets the friction that damps the movement of the value.
|
||||
A typical value is 0.08; higher values indicate more friction.
|
||||
*/
|
||||
void setFriction (double newFriction) noexcept
|
||||
{
|
||||
damping = 1.0 - newFriction;
|
||||
}
|
||||
|
||||
/** Sets the minimum velocity of the movement. Any velocity that's slower than
|
||||
this will stop the animation. The default is 0.05. */
|
||||
void setMinimumVelocity (double newMinimumVelocityToUse) noexcept
|
||||
{
|
||||
minimumVelocity = newMinimumVelocityToUse;
|
||||
}
|
||||
|
||||
/** Called by the AnimatedPosition class. This tells us the position and
|
||||
velocity at which the user is about to release the object.
|
||||
The velocity is measured in units/second.
|
||||
*/
|
||||
void releasedWithVelocity (double /*position*/, double releaseVelocity) noexcept
|
||||
{
|
||||
velocity = releaseVelocity;
|
||||
}
|
||||
|
||||
/** Called by the AnimatedPosition class to get the new position, after
|
||||
the given time has elapsed.
|
||||
*/
|
||||
double getNextPosition (double oldPos, double elapsedSeconds) noexcept
|
||||
{
|
||||
velocity *= damping;
|
||||
|
||||
if (std::abs (velocity) < minimumVelocity)
|
||||
velocity = 0;
|
||||
|
||||
return oldPos + velocity * elapsedSeconds;
|
||||
}
|
||||
|
||||
/** Called by the AnimatedPosition class to check whether the object
|
||||
is now stationary.
|
||||
*/
|
||||
bool isStopped (double /*position*/) const noexcept
|
||||
{
|
||||
return velocity == 0.0;
|
||||
}
|
||||
|
||||
private:
|
||||
double velocity = 0, damping = 0.92, minimumVelocity = 0.05;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** A behaviour that gravitates an AnimatedPosition object towards the nearest
|
||||
integer position when released.
|
||||
|
||||
This class is intended to be used as a template parameter to the
|
||||
AnimatedPosition class. It's handy when using an AnimatedPosition to show a
|
||||
series of pages, because it allows the pages can be scrolled smoothly, but when
|
||||
released, snaps back to show a whole page.
|
||||
|
||||
@see AnimatedPosition
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
struct SnapToPageBoundaries
|
||||
{
|
||||
SnapToPageBoundaries() = default;
|
||||
|
||||
/** Called by the AnimatedPosition class. This tells us the position and
|
||||
velocity at which the user is about to release the object.
|
||||
The velocity is measured in units/second.
|
||||
*/
|
||||
void releasedWithVelocity (double position, double releaseVelocity) noexcept
|
||||
{
|
||||
targetSnapPosition = std::floor (position + 0.5);
|
||||
|
||||
if (releaseVelocity > 1.0 && targetSnapPosition < position) ++targetSnapPosition;
|
||||
if (releaseVelocity < -1.0 && targetSnapPosition > position) --targetSnapPosition;
|
||||
}
|
||||
|
||||
/** Called by the AnimatedPosition class to get the new position, after
|
||||
the given time has elapsed.
|
||||
*/
|
||||
double getNextPosition (double oldPos, double elapsedSeconds) const noexcept
|
||||
{
|
||||
if (isStopped (oldPos))
|
||||
return targetSnapPosition;
|
||||
|
||||
const double snapSpeed = 10.0;
|
||||
const double velocity = (targetSnapPosition - oldPos) * snapSpeed;
|
||||
const double newPos = oldPos + velocity * elapsedSeconds;
|
||||
|
||||
return isStopped (newPos) ? targetSnapPosition : newPos;
|
||||
}
|
||||
|
||||
/** Called by the AnimatedPosition class to check whether the object
|
||||
is now stationary.
|
||||
*/
|
||||
bool isStopped (double position) const noexcept
|
||||
{
|
||||
return std::abs (targetSnapPosition - position) < 0.001;
|
||||
}
|
||||
|
||||
private:
|
||||
double targetSnapPosition = 0.0;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace juce
|
354
deps/juce/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp
vendored
Normal file
354
deps/juce/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp
vendored
Normal file
@ -0,0 +1,354 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class ComponentAnimator::AnimationTask
|
||||
{
|
||||
public:
|
||||
AnimationTask (Component* c) noexcept : component (c) {}
|
||||
|
||||
~AnimationTask()
|
||||
{
|
||||
proxy.deleteAndZero();
|
||||
}
|
||||
|
||||
void reset (const Rectangle<int>& finalBounds,
|
||||
float finalAlpha,
|
||||
int millisecondsToSpendMoving,
|
||||
bool useProxyComponent,
|
||||
double startSpd, double endSpd)
|
||||
{
|
||||
msElapsed = 0;
|
||||
msTotal = jmax (1, millisecondsToSpendMoving);
|
||||
lastProgress = 0;
|
||||
destination = finalBounds;
|
||||
destAlpha = finalAlpha;
|
||||
|
||||
isMoving = (finalBounds != component->getBounds());
|
||||
isChangingAlpha = (finalAlpha != component->getAlpha());
|
||||
|
||||
left = component->getX();
|
||||
top = component->getY();
|
||||
right = component->getRight();
|
||||
bottom = component->getBottom();
|
||||
alpha = component->getAlpha();
|
||||
|
||||
const double invTotalDistance = 4.0 / (startSpd + endSpd + 2.0);
|
||||
startSpeed = jmax (0.0, startSpd * invTotalDistance);
|
||||
midSpeed = invTotalDistance;
|
||||
endSpeed = jmax (0.0, endSpd * invTotalDistance);
|
||||
|
||||
proxy.deleteAndZero();
|
||||
|
||||
if (useProxyComponent)
|
||||
proxy = new ProxyComponent (*component);
|
||||
|
||||
component->setVisible (! useProxyComponent);
|
||||
}
|
||||
|
||||
bool useTimeslice (const int elapsed)
|
||||
{
|
||||
if (auto* c = proxy != nullptr ? proxy.getComponent()
|
||||
: component.get())
|
||||
{
|
||||
msElapsed += elapsed;
|
||||
double newProgress = msElapsed / (double) msTotal;
|
||||
|
||||
if (newProgress >= 0 && newProgress < 1.0)
|
||||
{
|
||||
const WeakReference<AnimationTask> weakRef (this);
|
||||
newProgress = timeToDistance (newProgress);
|
||||
const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
|
||||
jassert (newProgress >= lastProgress);
|
||||
lastProgress = newProgress;
|
||||
|
||||
if (delta < 1.0)
|
||||
{
|
||||
bool stillBusy = false;
|
||||
|
||||
if (isMoving)
|
||||
{
|
||||
left += (destination.getX() - left) * delta;
|
||||
top += (destination.getY() - top) * delta;
|
||||
right += (destination.getRight() - right) * delta;
|
||||
bottom += (destination.getBottom() - bottom) * delta;
|
||||
|
||||
const Rectangle<int> newBounds (roundToInt (left),
|
||||
roundToInt (top),
|
||||
roundToInt (right - left),
|
||||
roundToInt (bottom - top));
|
||||
|
||||
if (newBounds != destination)
|
||||
{
|
||||
c->setBounds (newBounds);
|
||||
stillBusy = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the animation was cancelled/deleted during
|
||||
// a callback during the setBounds method
|
||||
if (weakRef.wasObjectDeleted())
|
||||
return false;
|
||||
|
||||
if (isChangingAlpha)
|
||||
{
|
||||
alpha += (destAlpha - alpha) * delta;
|
||||
c->setAlpha ((float) alpha);
|
||||
stillBusy = true;
|
||||
}
|
||||
|
||||
if (stillBusy)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moveToFinalDestination();
|
||||
return false;
|
||||
}
|
||||
|
||||
void moveToFinalDestination()
|
||||
{
|
||||
if (component != nullptr)
|
||||
{
|
||||
const WeakReference<AnimationTask> weakRef (this);
|
||||
component->setAlpha ((float) destAlpha);
|
||||
component->setBounds (destination);
|
||||
|
||||
if (! weakRef.wasObjectDeleted())
|
||||
if (proxy != nullptr)
|
||||
component->setVisible (destAlpha > 0);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct ProxyComponent : public Component
|
||||
{
|
||||
ProxyComponent (Component& c)
|
||||
{
|
||||
setWantsKeyboardFocus (false);
|
||||
setBounds (c.getBounds());
|
||||
setTransform (c.getTransform());
|
||||
setAlpha (c.getAlpha());
|
||||
setInterceptsMouseClicks (false, false);
|
||||
|
||||
if (auto* parent = c.getParentComponent())
|
||||
parent->addAndMakeVisible (this);
|
||||
else if (c.isOnDesktop() && c.getPeer() != nullptr)
|
||||
addToDesktop (c.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses);
|
||||
else
|
||||
jassertfalse; // seem to be trying to animate a component that's not visible..
|
||||
|
||||
auto scale = (float) Desktop::getInstance().getDisplays().getDisplayForRect (getScreenBounds())->scale
|
||||
* Component::getApproximateScaleFactorForComponent (&c);
|
||||
|
||||
image = c.createComponentSnapshot (c.getLocalBounds(), false, scale);
|
||||
|
||||
setVisible (true);
|
||||
toBehind (&c);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.setOpacity (1.0f);
|
||||
g.drawImageTransformed (image, AffineTransform::scale ((float) getWidth() / (float) jmax (1, image.getWidth()),
|
||||
(float) getHeight() / (float) jmax (1, image.getHeight())), false);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return createIgnoredAccessibilityHandler (*this);
|
||||
}
|
||||
|
||||
Image image;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent)
|
||||
};
|
||||
|
||||
WeakReference<Component> component;
|
||||
Component::SafePointer<Component> proxy;
|
||||
|
||||
Rectangle<int> destination;
|
||||
double destAlpha;
|
||||
|
||||
int msElapsed, msTotal;
|
||||
double startSpeed, midSpeed, endSpeed, lastProgress;
|
||||
double left, top, right, bottom, alpha;
|
||||
bool isMoving, isChangingAlpha;
|
||||
|
||||
private:
|
||||
double timeToDistance (const double time) const noexcept
|
||||
{
|
||||
return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed))
|
||||
: 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed))
|
||||
+ (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed));
|
||||
}
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (AnimationTask)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimationTask)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ComponentAnimator::ComponentAnimator() : lastTime (0) {}
|
||||
ComponentAnimator::~ComponentAnimator() {}
|
||||
|
||||
//==============================================================================
|
||||
ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const noexcept
|
||||
{
|
||||
for (int i = tasks.size(); --i >= 0;)
|
||||
if (component == tasks.getUnchecked(i)->component.get())
|
||||
return tasks.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ComponentAnimator::animateComponent (Component* const component,
|
||||
const Rectangle<int>& finalBounds,
|
||||
const float finalAlpha,
|
||||
const int millisecondsToSpendMoving,
|
||||
const bool useProxyComponent,
|
||||
const double startSpeed,
|
||||
const double endSpeed)
|
||||
{
|
||||
// the speeds must be 0 or greater!
|
||||
jassert (startSpeed >= 0 && endSpeed >= 0);
|
||||
|
||||
if (component != nullptr)
|
||||
{
|
||||
auto* at = findTaskFor (component);
|
||||
|
||||
if (at == nullptr)
|
||||
{
|
||||
at = new AnimationTask (component);
|
||||
tasks.add (at);
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
at->reset (finalBounds, finalAlpha, millisecondsToSpendMoving,
|
||||
useProxyComponent, startSpeed, endSpeed);
|
||||
|
||||
if (! isTimerRunning())
|
||||
{
|
||||
lastTime = Time::getMillisecondCounter();
|
||||
startTimerHz (50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentAnimator::fadeOut (Component* component, int millisecondsToTake)
|
||||
{
|
||||
if (component != nullptr)
|
||||
{
|
||||
if (component->isShowing() && millisecondsToTake > 0)
|
||||
animateComponent (component, component->getBounds(), 0.0f, millisecondsToTake, true, 1.0, 1.0);
|
||||
|
||||
component->setVisible (false);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentAnimator::fadeIn (Component* component, int millisecondsToTake)
|
||||
{
|
||||
if (component != nullptr && ! (component->isVisible() && component->getAlpha() == 1.0f))
|
||||
{
|
||||
component->setAlpha (0.0f);
|
||||
component->setVisible (true);
|
||||
animateComponent (component, component->getBounds(), 1.0f, millisecondsToTake, false, 1.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
|
||||
{
|
||||
if (tasks.size() > 0)
|
||||
{
|
||||
if (moveComponentsToTheirFinalPositions)
|
||||
for (int i = tasks.size(); --i >= 0;)
|
||||
tasks.getUnchecked(i)->moveToFinalDestination();
|
||||
|
||||
tasks.clear();
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentAnimator::cancelAnimation (Component* const component,
|
||||
const bool moveComponentToItsFinalPosition)
|
||||
{
|
||||
if (auto* at = findTaskFor (component))
|
||||
{
|
||||
if (moveComponentToItsFinalPosition)
|
||||
at->moveToFinalDestination();
|
||||
|
||||
tasks.removeObject (at);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
|
||||
{
|
||||
jassert (component != nullptr);
|
||||
|
||||
if (auto* at = findTaskFor (component))
|
||||
return at->destination;
|
||||
|
||||
return component->getBounds();
|
||||
}
|
||||
|
||||
bool ComponentAnimator::isAnimating (Component* component) const noexcept
|
||||
{
|
||||
return findTaskFor (component) != nullptr;
|
||||
}
|
||||
|
||||
bool ComponentAnimator::isAnimating() const noexcept
|
||||
{
|
||||
return tasks.size() != 0;
|
||||
}
|
||||
|
||||
void ComponentAnimator::timerCallback()
|
||||
{
|
||||
auto timeNow = Time::getMillisecondCounter();
|
||||
|
||||
if (lastTime == 0)
|
||||
lastTime = timeNow;
|
||||
|
||||
auto elapsed = (int) (timeNow - lastTime);
|
||||
|
||||
for (auto* task : Array<AnimationTask*> (tasks.begin(), tasks.size()))
|
||||
{
|
||||
if (tasks.contains (task) && ! task->useTimeslice (elapsed))
|
||||
{
|
||||
tasks.removeObject (task);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
lastTime = timeNow;
|
||||
|
||||
if (tasks.size() == 0)
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
} // namespace juce
|
162
deps/juce/modules/juce_gui_basics/layout/juce_ComponentAnimator.h
vendored
Normal file
162
deps/juce/modules/juce_gui_basics/layout/juce_ComponentAnimator.h
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Animates a set of components, moving them to a new position and/or fading their
|
||||
alpha levels.
|
||||
|
||||
To animate a component, create a ComponentAnimator instance or (preferably) use the
|
||||
global animator object provided by Desktop::getAnimator(), and call its animateComponent()
|
||||
method to commence the movement.
|
||||
|
||||
If you're using your own ComponentAnimator instance, you'll need to make sure it isn't
|
||||
deleted before it finishes moving the components, or they'll be abandoned before reaching their
|
||||
destinations.
|
||||
|
||||
It's ok to delete components while they're being animated - the animator will detect this
|
||||
and safely stop using them.
|
||||
|
||||
The class is a ChangeBroadcaster and sends a notification when any components
|
||||
start or finish being animated.
|
||||
|
||||
@see Desktop::getAnimator
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ComponentAnimator : public ChangeBroadcaster,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ComponentAnimator. */
|
||||
ComponentAnimator();
|
||||
|
||||
/** Destructor. */
|
||||
~ComponentAnimator() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts a component moving from its current position to a specified position.
|
||||
|
||||
If the component is already in the middle of an animation, that will be abandoned,
|
||||
and a new animation will begin, moving the component from its current location.
|
||||
|
||||
The start and end speed parameters let you apply some acceleration to the component's
|
||||
movement.
|
||||
|
||||
@param component the component to move
|
||||
@param finalBounds the destination bounds to which the component should move. To leave the
|
||||
component in the same place, just pass component->getBounds() for this value
|
||||
@param finalAlpha the alpha value that the component should have at the end of the animation
|
||||
@param animationDurationMilliseconds how long the animation should last, in milliseconds
|
||||
@param useProxyComponent if true, this means the component should be replaced by an internally
|
||||
managed temporary component which is a snapshot of the original component.
|
||||
This avoids the component having to paint itself as it moves, so may
|
||||
be more efficient. This option also allows you to delete the original
|
||||
component immediately after starting the animation, because the animation
|
||||
can proceed without it. If you use a proxy, the original component will be
|
||||
made invisible by this call, and then will become visible again at the end
|
||||
of the animation. It'll also mean that the proxy component will be temporarily
|
||||
added to the component's parent, so avoid it if this might confuse the parent
|
||||
component, or if there's a chance the parent might decide to delete its children.
|
||||
@param startSpeed a value to indicate the relative start speed of the animation. If this is 0,
|
||||
the component will start by accelerating from rest; higher values mean that it
|
||||
will have an initial speed greater than zero. If the value is greater than 1, it
|
||||
will decelerate towards the middle of its journey. To move the component at a
|
||||
constant rate for its entire animation, set both the start and end speeds to 1.0
|
||||
@param endSpeed a relative speed at which the component should be moving when the animation finishes.
|
||||
If this is 0, the component will decelerate to a standstill at its final position;
|
||||
higher values mean the component will still be moving when it stops. To move the component
|
||||
at a constant rate for its entire animation, set both the start and end speeds to 1.0
|
||||
*/
|
||||
void animateComponent (Component* component,
|
||||
const Rectangle<int>& finalBounds,
|
||||
float finalAlpha,
|
||||
int animationDurationMilliseconds,
|
||||
bool useProxyComponent,
|
||||
double startSpeed,
|
||||
double endSpeed);
|
||||
|
||||
/** Begins a fade-out of this components alpha level.
|
||||
This is a quick way of invoking animateComponent() with a target alpha value of 0.0f, using
|
||||
a proxy. You're safe to delete the component after calling this method, and this won't
|
||||
interfere with the animation's progress.
|
||||
*/
|
||||
void fadeOut (Component* component, int millisecondsToTake);
|
||||
|
||||
/** Begins a fade-in of a component.
|
||||
This is a quick way of invoking animateComponent() with a target alpha value of 1.0f.
|
||||
*/
|
||||
void fadeIn (Component* component, int millisecondsToTake);
|
||||
|
||||
/** Stops a component if it's currently being animated.
|
||||
|
||||
If moveComponentToItsFinalPosition is true, then the component will
|
||||
be immediately moved to its destination position and size. If false, it will be
|
||||
left in whatever location it currently occupies.
|
||||
*/
|
||||
void cancelAnimation (Component* component,
|
||||
bool moveComponentToItsFinalPosition);
|
||||
|
||||
/** Clears all of the active animations.
|
||||
|
||||
If moveComponentsToTheirFinalPositions is true, all the components will
|
||||
be immediately set to their final positions. If false, they will be
|
||||
left in whatever locations they currently occupy.
|
||||
*/
|
||||
void cancelAllAnimations (bool moveComponentsToTheirFinalPositions);
|
||||
|
||||
/** Returns the destination position for a component.
|
||||
|
||||
If the component is being animated, this will return the target position that
|
||||
was specified when animateComponent() was called.
|
||||
|
||||
If the specified component isn't currently being animated, this method will just
|
||||
return its current position.
|
||||
*/
|
||||
Rectangle<int> getComponentDestination (Component* component);
|
||||
|
||||
/** Returns true if the specified component is currently being animated. */
|
||||
bool isAnimating (Component* component) const noexcept;
|
||||
|
||||
/** Returns true if any components are currently being animated. */
|
||||
bool isAnimating() const noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class AnimationTask;
|
||||
OwnedArray<AnimationTask> tasks;
|
||||
uint32 lastTime;
|
||||
|
||||
AnimationTask* findTaskFor (Component*) const noexcept;
|
||||
void timerCallback() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentAnimator)
|
||||
};
|
||||
|
||||
} // namespace juce
|
296
deps/juce/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp
vendored
Normal file
296
deps/juce/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp
vendored
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ComponentBoundsConstrainer::ComponentBoundsConstrainer() noexcept {}
|
||||
ComponentBoundsConstrainer::~ComponentBoundsConstrainer() {}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentBoundsConstrainer::setMinimumWidth (int minimumWidth) noexcept { minW = minimumWidth; }
|
||||
void ComponentBoundsConstrainer::setMaximumWidth (int maximumWidth) noexcept { maxW = maximumWidth; }
|
||||
void ComponentBoundsConstrainer::setMinimumHeight (int minimumHeight) noexcept { minH = minimumHeight; }
|
||||
void ComponentBoundsConstrainer::setMaximumHeight (int maximumHeight) noexcept { maxH = maximumHeight; }
|
||||
|
||||
void ComponentBoundsConstrainer::setMinimumSize (int minimumWidth, int minimumHeight) noexcept
|
||||
{
|
||||
jassert (maxW >= minimumWidth);
|
||||
jassert (maxH >= minimumHeight);
|
||||
jassert (minimumWidth > 0 && minimumHeight > 0);
|
||||
|
||||
minW = minimumWidth;
|
||||
minH = minimumHeight;
|
||||
|
||||
if (minW > maxW) maxW = minW;
|
||||
if (minH > maxH) maxH = minH;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setMaximumSize (int maximumWidth, int maximumHeight) noexcept
|
||||
{
|
||||
jassert (maximumWidth >= minW);
|
||||
jassert (maximumHeight >= minH);
|
||||
jassert (maximumWidth > 0 && maximumHeight > 0);
|
||||
|
||||
maxW = jmax (minW, maximumWidth);
|
||||
maxH = jmax (minH, maximumHeight);
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setSizeLimits (int minimumWidth,
|
||||
int minimumHeight,
|
||||
int maximumWidth,
|
||||
int maximumHeight) noexcept
|
||||
{
|
||||
jassert (maximumWidth >= minimumWidth);
|
||||
jassert (maximumHeight >= minimumHeight);
|
||||
jassert (maximumWidth > 0 && maximumHeight > 0);
|
||||
jassert (minimumWidth > 0 && minimumHeight > 0);
|
||||
|
||||
minW = jmax (0, minimumWidth);
|
||||
minH = jmax (0, minimumHeight);
|
||||
maxW = jmax (minW, maximumWidth);
|
||||
maxH = jmax (minH, maximumHeight);
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setMinimumOnscreenAmounts (int minimumWhenOffTheTop,
|
||||
int minimumWhenOffTheLeft,
|
||||
int minimumWhenOffTheBottom,
|
||||
int minimumWhenOffTheRight) noexcept
|
||||
{
|
||||
minOffTop = minimumWhenOffTheTop;
|
||||
minOffLeft = minimumWhenOffTheLeft;
|
||||
minOffBottom = minimumWhenOffTheBottom;
|
||||
minOffRight = minimumWhenOffTheRight;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setFixedAspectRatio (double widthOverHeight) noexcept
|
||||
{
|
||||
aspectRatio = jmax (0.0, widthOverHeight);
|
||||
}
|
||||
|
||||
double ComponentBoundsConstrainer::getFixedAspectRatio() const noexcept
|
||||
{
|
||||
return aspectRatio;
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::setBoundsForComponent (Component* component,
|
||||
Rectangle<int> targetBounds,
|
||||
bool isStretchingTop,
|
||||
bool isStretchingLeft,
|
||||
bool isStretchingBottom,
|
||||
bool isStretchingRight)
|
||||
{
|
||||
jassert (component != nullptr);
|
||||
|
||||
Rectangle<int> limits, bounds (targetBounds);
|
||||
BorderSize<int> border;
|
||||
|
||||
if (auto* parent = component->getParentComponent())
|
||||
{
|
||||
limits.setSize (parent->getWidth(), parent->getHeight());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto* peer = component->getPeer())
|
||||
border = peer->getFrameSize();
|
||||
|
||||
auto screenBounds = Desktop::getInstance().getDisplays().getDisplayForPoint (targetBounds.getCentre())->userArea;
|
||||
|
||||
limits = component->getLocalArea (nullptr, screenBounds) + component->getPosition();
|
||||
}
|
||||
|
||||
border.addTo (bounds);
|
||||
|
||||
checkBounds (bounds,
|
||||
border.addedTo (component->getBounds()), limits,
|
||||
isStretchingTop, isStretchingLeft,
|
||||
isStretchingBottom, isStretchingRight);
|
||||
|
||||
border.subtractFrom (bounds);
|
||||
|
||||
applyBoundsToComponent (*component, bounds);
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::checkComponentBounds (Component* component)
|
||||
{
|
||||
setBoundsForComponent (component, component->getBounds(),
|
||||
false, false, false, false);
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::applyBoundsToComponent (Component& component, Rectangle<int> bounds)
|
||||
{
|
||||
if (auto* positioner = component.getPositioner())
|
||||
positioner->applyNewBounds (bounds);
|
||||
else
|
||||
component.setBounds (bounds);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentBoundsConstrainer::resizeStart()
|
||||
{
|
||||
}
|
||||
|
||||
void ComponentBoundsConstrainer::resizeEnd()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentBoundsConstrainer::checkBounds (Rectangle<int>& bounds,
|
||||
const Rectangle<int>& old,
|
||||
const Rectangle<int>& limits,
|
||||
bool isStretchingTop,
|
||||
bool isStretchingLeft,
|
||||
bool isStretchingBottom,
|
||||
bool isStretchingRight)
|
||||
{
|
||||
if (isStretchingLeft)
|
||||
bounds.setLeft (jlimit (old.getRight() - maxW, old.getRight() - minW, bounds.getX()));
|
||||
else
|
||||
bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
|
||||
|
||||
if (isStretchingTop)
|
||||
bounds.setTop (jlimit (old.getBottom() - maxH, old.getBottom() - minH, bounds.getY()));
|
||||
else
|
||||
bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
|
||||
|
||||
if (bounds.isEmpty())
|
||||
return;
|
||||
|
||||
if (minOffTop > 0)
|
||||
{
|
||||
const int limit = limits.getY() + jmin (minOffTop - bounds.getHeight(), 0);
|
||||
|
||||
if (bounds.getY() < limit)
|
||||
{
|
||||
if (isStretchingTop)
|
||||
bounds.setTop (limits.getY());
|
||||
else
|
||||
bounds.setY (limit);
|
||||
}
|
||||
}
|
||||
|
||||
if (minOffLeft > 0)
|
||||
{
|
||||
const int limit = limits.getX() + jmin (minOffLeft - bounds.getWidth(), 0);
|
||||
|
||||
if (bounds.getX() < limit)
|
||||
{
|
||||
if (isStretchingLeft)
|
||||
bounds.setLeft (limits.getX());
|
||||
else
|
||||
bounds.setX (limit);
|
||||
}
|
||||
}
|
||||
|
||||
if (minOffBottom > 0)
|
||||
{
|
||||
const int limit = limits.getBottom() - jmin (minOffBottom, bounds.getHeight());
|
||||
|
||||
if (bounds.getY() > limit)
|
||||
{
|
||||
if (isStretchingBottom)
|
||||
bounds.setBottom (limits.getBottom());
|
||||
else
|
||||
bounds.setY (limit);
|
||||
}
|
||||
}
|
||||
|
||||
if (minOffRight > 0)
|
||||
{
|
||||
const int limit = limits.getRight() - jmin (minOffRight, bounds.getWidth());
|
||||
|
||||
if (bounds.getX() > limit)
|
||||
{
|
||||
if (isStretchingRight)
|
||||
bounds.setRight (limits.getRight());
|
||||
else
|
||||
bounds.setX (limit);
|
||||
}
|
||||
}
|
||||
|
||||
// constrain the aspect ratio if one has been specified..
|
||||
if (aspectRatio > 0.0)
|
||||
{
|
||||
bool adjustWidth;
|
||||
|
||||
if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
|
||||
{
|
||||
adjustWidth = true;
|
||||
}
|
||||
else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
|
||||
{
|
||||
adjustWidth = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const double oldRatio = (old.getHeight() > 0) ? std::abs (old.getWidth() / (double) old.getHeight()) : 0.0;
|
||||
const double newRatio = std::abs (bounds.getWidth() / (double) bounds.getHeight());
|
||||
|
||||
adjustWidth = (oldRatio > newRatio);
|
||||
}
|
||||
|
||||
if (adjustWidth)
|
||||
{
|
||||
bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
|
||||
|
||||
if (bounds.getWidth() > maxW || bounds.getWidth() < minW)
|
||||
{
|
||||
bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
|
||||
bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
|
||||
|
||||
if (bounds.getHeight() > maxH || bounds.getHeight() < minH)
|
||||
{
|
||||
bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
|
||||
bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
|
||||
}
|
||||
}
|
||||
|
||||
if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
|
||||
{
|
||||
bounds.setX (old.getX() + (old.getWidth() - bounds.getWidth()) / 2);
|
||||
}
|
||||
else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
|
||||
{
|
||||
bounds.setY (old.getY() + (old.getHeight() - bounds.getHeight()) / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isStretchingLeft)
|
||||
bounds.setX (old.getRight() - bounds.getWidth());
|
||||
|
||||
if (isStretchingTop)
|
||||
bounds.setY (old.getBottom() - bounds.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
jassert (! bounds.isEmpty());
|
||||
}
|
||||
|
||||
} // namespace juce
|
197
deps/juce/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.h
vendored
Normal file
197
deps/juce/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.h
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class that imposes restrictions on a Component's size or position.
|
||||
|
||||
This is used by classes such as ResizableCornerComponent,
|
||||
ResizableBorderComponent and ResizableWindow.
|
||||
|
||||
The base class can impose some basic size and position limits, but you can
|
||||
also subclass this for custom uses.
|
||||
|
||||
@see ResizableCornerComponent, ResizableBorderComponent, ResizableWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ComponentBoundsConstrainer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** When first created, the object will not impose any restrictions on the components. */
|
||||
ComponentBoundsConstrainer() noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ComponentBoundsConstrainer();
|
||||
|
||||
//==============================================================================
|
||||
/** Imposes a minimum width limit. */
|
||||
void setMinimumWidth (int minimumWidth) noexcept;
|
||||
|
||||
/** Returns the current minimum width. */
|
||||
int getMinimumWidth() const noexcept { return minW; }
|
||||
|
||||
/** Imposes a maximum width limit. */
|
||||
void setMaximumWidth (int maximumWidth) noexcept;
|
||||
|
||||
/** Returns the current maximum width. */
|
||||
int getMaximumWidth() const noexcept { return maxW; }
|
||||
|
||||
/** Imposes a minimum height limit. */
|
||||
void setMinimumHeight (int minimumHeight) noexcept;
|
||||
|
||||
/** Returns the current minimum height. */
|
||||
int getMinimumHeight() const noexcept { return minH; }
|
||||
|
||||
/** Imposes a maximum height limit. */
|
||||
void setMaximumHeight (int maximumHeight) noexcept;
|
||||
|
||||
/** Returns the current maximum height. */
|
||||
int getMaximumHeight() const noexcept { return maxH; }
|
||||
|
||||
/** Imposes a minimum width and height limit. */
|
||||
void setMinimumSize (int minimumWidth,
|
||||
int minimumHeight) noexcept;
|
||||
|
||||
/** Imposes a maximum width and height limit. */
|
||||
void setMaximumSize (int maximumWidth,
|
||||
int maximumHeight) noexcept;
|
||||
|
||||
/** Set all the maximum and minimum dimensions. */
|
||||
void setSizeLimits (int minimumWidth,
|
||||
int minimumHeight,
|
||||
int maximumWidth,
|
||||
int maximumHeight) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the amount by which the component is allowed to go off-screen.
|
||||
|
||||
The values indicate how many pixels must remain on-screen when dragged off
|
||||
one of its parent's edges, so e.g. if minimumWhenOffTheTop is set to 10, then
|
||||
when the component goes off the top of the screen, its y-position will be
|
||||
clipped so that there are always at least 10 pixels on-screen. In other words,
|
||||
the lowest y-position it can take would be (10 - the component's height).
|
||||
|
||||
If you pass 0 or less for one of these amounts, the component is allowed
|
||||
to move beyond that edge completely, with no restrictions at all.
|
||||
|
||||
If you pass a very large number (i.e. larger that the dimensions of the
|
||||
component itself), then the component won't be allowed to overlap that
|
||||
edge at all. So e.g. setting minimumWhenOffTheLeft to 0xffffff will mean that
|
||||
the component will bump into the left side of the screen and go no further.
|
||||
*/
|
||||
void setMinimumOnscreenAmounts (int minimumWhenOffTheTop,
|
||||
int minimumWhenOffTheLeft,
|
||||
int minimumWhenOffTheBottom,
|
||||
int minimumWhenOffTheRight) noexcept;
|
||||
|
||||
|
||||
/** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
|
||||
int getMinimumWhenOffTheTop() const noexcept { return minOffTop; }
|
||||
/** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
|
||||
int getMinimumWhenOffTheLeft() const noexcept { return minOffLeft; }
|
||||
/** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
|
||||
int getMinimumWhenOffTheBottom() const noexcept { return minOffBottom; }
|
||||
/** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
|
||||
int getMinimumWhenOffTheRight() const noexcept { return minOffRight; }
|
||||
|
||||
//==============================================================================
|
||||
/** Specifies a width-to-height ratio that the resizer should always maintain.
|
||||
|
||||
If the value is 0, no aspect ratio is enforced. If it's non-zero, the width
|
||||
will always be maintained as this multiple of the height.
|
||||
|
||||
@see setResizeLimits
|
||||
*/
|
||||
void setFixedAspectRatio (double widthOverHeight) noexcept;
|
||||
|
||||
/** Returns the aspect ratio that was set with setFixedAspectRatio().
|
||||
|
||||
If no aspect ratio is being enforced, this will return 0.
|
||||
*/
|
||||
double getFixedAspectRatio() const noexcept;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** This callback changes the given coordinates to impose whatever the current
|
||||
constraints are set to be.
|
||||
|
||||
@param bounds the target position that should be examined and adjusted
|
||||
@param previousBounds the component's current size
|
||||
@param limits the region in which the component can be positioned
|
||||
@param isStretchingTop whether the top edge of the component is being resized
|
||||
@param isStretchingLeft whether the left edge of the component is being resized
|
||||
@param isStretchingBottom whether the bottom edge of the component is being resized
|
||||
@param isStretchingRight whether the right edge of the component is being resized
|
||||
*/
|
||||
virtual void checkBounds (Rectangle<int>& bounds,
|
||||
const Rectangle<int>& previousBounds,
|
||||
const Rectangle<int>& limits,
|
||||
bool isStretchingTop,
|
||||
bool isStretchingLeft,
|
||||
bool isStretchingBottom,
|
||||
bool isStretchingRight);
|
||||
|
||||
/** This callback happens when the resizer is about to start dragging. */
|
||||
virtual void resizeStart();
|
||||
|
||||
/** This callback happens when the resizer has finished dragging. */
|
||||
virtual void resizeEnd();
|
||||
|
||||
/** Checks the given bounds, and then sets the component to the corrected size. */
|
||||
void setBoundsForComponent (Component* component,
|
||||
Rectangle<int> bounds,
|
||||
bool isStretchingTop,
|
||||
bool isStretchingLeft,
|
||||
bool isStretchingBottom,
|
||||
bool isStretchingRight);
|
||||
|
||||
/** Performs a check on the current size of a component, and moves or resizes
|
||||
it if it fails the constraints.
|
||||
*/
|
||||
void checkComponentBounds (Component* component);
|
||||
|
||||
/** Called by setBoundsForComponent() to apply a new constrained size to a
|
||||
component.
|
||||
|
||||
By default this just calls setBounds(), but is virtual in case it's needed for
|
||||
extremely cunning purposes.
|
||||
*/
|
||||
virtual void applyBoundsToComponent (Component&, Rectangle<int> bounds);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int minW = 0, maxW = 0x3fffffff, minH = 0, maxH = 0x3fffffff;
|
||||
int minOffTop = 0, minOffLeft = 0, minOffBottom = 0, minOffRight = 0;
|
||||
double aspectRatio = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBoundsConstrainer)
|
||||
};
|
||||
|
||||
} // namespace juce
|
286
deps/juce/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp
vendored
Normal file
286
deps/juce/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp
vendored
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace ComponentBuilderHelpers
|
||||
{
|
||||
static String getStateId (const ValueTree& state)
|
||||
{
|
||||
return state [ComponentBuilder::idProperty].toString();
|
||||
}
|
||||
|
||||
static Component* removeComponentWithID (OwnedArray<Component>& components, const String& compId)
|
||||
{
|
||||
jassert (compId.isNotEmpty());
|
||||
|
||||
for (int i = components.size(); --i >= 0;)
|
||||
{
|
||||
Component* const c = components.getUnchecked (i);
|
||||
|
||||
if (c->getComponentID() == compId)
|
||||
return components.removeAndReturn (i);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Component* findComponentWithID (Component& c, const String& compId)
|
||||
{
|
||||
jassert (compId.isNotEmpty());
|
||||
if (c.getComponentID() == compId)
|
||||
return &c;
|
||||
|
||||
for (auto* child : c.getChildren())
|
||||
if (auto* found = findComponentWithID (*child, compId))
|
||||
return found;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Component* createNewComponent (ComponentBuilder::TypeHandler& type,
|
||||
const ValueTree& state, Component* parent)
|
||||
{
|
||||
Component* const c = type.addNewComponentFromState (state, parent);
|
||||
jassert (c != nullptr && c->getParentComponent() == parent);
|
||||
c->setComponentID (getStateId (state));
|
||||
return c;
|
||||
}
|
||||
|
||||
static void updateComponent (ComponentBuilder& builder, const ValueTree& state)
|
||||
{
|
||||
if (Component* topLevelComp = builder.getManagedComponent())
|
||||
{
|
||||
ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state);
|
||||
const String uid (getStateId (state));
|
||||
|
||||
if (type == nullptr || uid.isEmpty())
|
||||
{
|
||||
// ..handle the case where a child of the actual state node has changed.
|
||||
if (state.getParent().isValid())
|
||||
updateComponent (builder, state.getParent());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Component* const changedComp = findComponentWithID (*topLevelComp, uid))
|
||||
type->updateComponentFromState (changedComp, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const Identifier ComponentBuilder::idProperty ("id");
|
||||
|
||||
ComponentBuilder::ComponentBuilder()
|
||||
: imageProvider (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ComponentBuilder::ComponentBuilder (const ValueTree& state_)
|
||||
: state (state_), imageProvider (nullptr)
|
||||
{
|
||||
state.addListener (this);
|
||||
}
|
||||
|
||||
ComponentBuilder::~ComponentBuilder()
|
||||
{
|
||||
state.removeListener (this);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
// Don't delete the managed component!! The builder owns that component, and will delete
|
||||
// it automatically when it gets deleted.
|
||||
jassert (componentRef.get() == component.get());
|
||||
#endif
|
||||
}
|
||||
|
||||
Component* ComponentBuilder::getManagedComponent()
|
||||
{
|
||||
if (component == nullptr)
|
||||
{
|
||||
component.reset (createComponent());
|
||||
|
||||
#if JUCE_DEBUG
|
||||
componentRef = component.get();
|
||||
#endif
|
||||
}
|
||||
|
||||
return component.get();
|
||||
}
|
||||
|
||||
Component* ComponentBuilder::createComponent()
|
||||
{
|
||||
jassert (types.size() > 0); // You need to register all the necessary types before you can load a component!
|
||||
|
||||
if (TypeHandler* const type = getHandlerForState (state))
|
||||
return ComponentBuilderHelpers::createNewComponent (*type, state, nullptr);
|
||||
|
||||
jassertfalse; // trying to create a component from an unknown type of ValueTree
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ComponentBuilder::registerTypeHandler (ComponentBuilder::TypeHandler* const type)
|
||||
{
|
||||
jassert (type != nullptr);
|
||||
|
||||
// Don't try to move your types around! Once a type has been added to a builder, the
|
||||
// builder owns it, and you should leave it alone!
|
||||
jassert (type->builder == nullptr);
|
||||
|
||||
types.add (type);
|
||||
type->builder = this;
|
||||
}
|
||||
|
||||
ComponentBuilder::TypeHandler* ComponentBuilder::getHandlerForState (const ValueTree& s) const
|
||||
{
|
||||
const Identifier targetType (s.getType());
|
||||
|
||||
for (int i = 0; i < types.size(); ++i)
|
||||
{
|
||||
TypeHandler* const t = types.getUnchecked(i);
|
||||
|
||||
if (t->type == targetType)
|
||||
return t;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ComponentBuilder::getNumHandlers() const noexcept
|
||||
{
|
||||
return types.size();
|
||||
}
|
||||
|
||||
ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) const noexcept
|
||||
{
|
||||
return types [index];
|
||||
}
|
||||
|
||||
void ComponentBuilder::registerStandardComponentTypes()
|
||||
{
|
||||
}
|
||||
|
||||
void ComponentBuilder::setImageProvider (ImageProvider* newImageProvider) noexcept
|
||||
{
|
||||
imageProvider = newImageProvider;
|
||||
}
|
||||
|
||||
ComponentBuilder::ImageProvider* ComponentBuilder::getImageProvider() const noexcept
|
||||
{
|
||||
return imageProvider;
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&, int)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree, int, int)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
void ComponentBuilder::valueTreeParentChanged (ValueTree& tree)
|
||||
{
|
||||
ComponentBuilderHelpers::updateComponent (*this, tree);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ComponentBuilder::TypeHandler::TypeHandler (const Identifier& valueTreeType)
|
||||
: type (valueTreeType), builder (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ComponentBuilder::TypeHandler::~TypeHandler()
|
||||
{
|
||||
}
|
||||
|
||||
ComponentBuilder* ComponentBuilder::TypeHandler::getBuilder() const noexcept
|
||||
{
|
||||
// A type handler needs to be registered with a ComponentBuilder before using it!
|
||||
jassert (builder != nullptr);
|
||||
return builder;
|
||||
}
|
||||
|
||||
void ComponentBuilder::updateChildComponents (Component& parent, const ValueTree& children)
|
||||
{
|
||||
using namespace ComponentBuilderHelpers;
|
||||
|
||||
auto numExistingChildComps = parent.getNumChildComponents();
|
||||
|
||||
Array<Component*> componentsInOrder;
|
||||
componentsInOrder.ensureStorageAllocated (numExistingChildComps);
|
||||
|
||||
{
|
||||
OwnedArray<Component> existingComponents;
|
||||
existingComponents.ensureStorageAllocated (numExistingChildComps);
|
||||
|
||||
for (int i = 0; i < numExistingChildComps; ++i)
|
||||
existingComponents.add (parent.getChildComponent (i));
|
||||
|
||||
auto newNumChildren = children.getNumChildren();
|
||||
|
||||
for (int i = 0; i < newNumChildren; ++i)
|
||||
{
|
||||
auto childState = children.getChild (i);
|
||||
auto* c = removeComponentWithID (existingComponents, getStateId (childState));
|
||||
|
||||
if (c == nullptr)
|
||||
{
|
||||
if (auto* type = getHandlerForState (childState))
|
||||
c = ComponentBuilderHelpers::createNewComponent (*type, childState, &parent);
|
||||
else
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
if (c != nullptr)
|
||||
componentsInOrder.add (c);
|
||||
}
|
||||
|
||||
// (remaining unused items in existingComponents get deleted here as it goes out of scope)
|
||||
}
|
||||
|
||||
// Make sure the z-order is correct..
|
||||
if (componentsInOrder.size() > 0)
|
||||
{
|
||||
componentsInOrder.getLast()->toFront (false);
|
||||
|
||||
for (int i = componentsInOrder.size() - 1; --i >= 0;)
|
||||
componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
247
deps/juce/modules/juce_gui_basics/layout/juce_ComponentBuilder.h
vendored
Normal file
247
deps/juce/modules/juce_gui_basics/layout/juce_ComponentBuilder.h
vendored
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Loads and maintains a tree of Components from a ValueTree that represents them.
|
||||
|
||||
To allow the state of a tree of components to be saved as a ValueTree and re-loaded,
|
||||
this class lets you register a set of type-handlers for the different components that
|
||||
are involved, and then uses these types to re-create a set of components from its
|
||||
stored state.
|
||||
|
||||
Essentially, to use this, you need to create a ComponentBuilder with your ValueTree,
|
||||
then use registerTypeHandler() to give it a set of type handlers that can cope with
|
||||
all the items in your tree. Then you can call getComponent() to build the component.
|
||||
Once you've got the component you can either take it and delete the ComponentBuilder
|
||||
object, or if you keep the ComponentBuilder around, it'll monitor any changes in the
|
||||
ValueTree and automatically update the component to reflect these changes.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ComponentBuilder : private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
/** Creates a ComponentBuilder that will use the given state.
|
||||
Once you've created your builder, you should use registerTypeHandler() to register some
|
||||
type handlers for it, and then you can call createComponent() or getManagedComponent()
|
||||
to get the actual component.
|
||||
*/
|
||||
explicit ComponentBuilder (const ValueTree& state);
|
||||
|
||||
/** Creates a builder that doesn't have a state object. */
|
||||
ComponentBuilder();
|
||||
|
||||
/** Destructor. */
|
||||
~ComponentBuilder() override;
|
||||
|
||||
/** This is the ValueTree data object that the builder is working with. */
|
||||
ValueTree state;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the builder's component (creating it if necessary).
|
||||
|
||||
The first time that this method is called, the builder will attempt to create a component
|
||||
from the ValueTree, so you must have registered some suitable type handlers before calling
|
||||
this. If there's a problem and the component can't be created, this method returns nullptr.
|
||||
|
||||
The component that is returned is owned by this ComponentBuilder, so you can put it inside
|
||||
your own parent components, but don't delete it! The ComponentBuilder will delete it automatically
|
||||
when the builder is destroyed. If you want to get a component that you can delete yourself,
|
||||
call createComponent() instead.
|
||||
|
||||
The ComponentBuilder will update this component if any changes are made to the ValueTree, so if
|
||||
there's a chance that the tree might change, be careful not to keep any pointers to sub-components,
|
||||
as they may be changed or removed.
|
||||
*/
|
||||
Component* getManagedComponent();
|
||||
|
||||
/** Creates and returns a new instance of the component that the ValueTree represents.
|
||||
The caller is responsible for using and deleting the object that is returned. Unlike
|
||||
getManagedComponent(), the component that is returned will not be updated by the builder.
|
||||
*/
|
||||
Component* createComponent();
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
The class is a base class for objects that manage the loading of a type of component
|
||||
from a ValueTree.
|
||||
|
||||
To store and re-load a tree of components as a ValueTree, each component type must have
|
||||
a TypeHandler to represent it.
|
||||
|
||||
@see ComponentBuilder::registerTypeHandler(), Drawable::registerDrawableTypeHandlers()
|
||||
*/
|
||||
class JUCE_API TypeHandler
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a TypeHandler.
|
||||
The valueTreeType must be the type name of the ValueTrees that this handler can parse.
|
||||
*/
|
||||
explicit TypeHandler (const Identifier& valueTreeType);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~TypeHandler();
|
||||
|
||||
/** Returns the type of the ValueTrees that this handler can parse. */
|
||||
const Identifier type;
|
||||
|
||||
/** Returns the builder that this type is registered with. */
|
||||
ComponentBuilder* getBuilder() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** This method must create a new component from the given state, add it to the specified
|
||||
parent component (which may be null), and return it.
|
||||
|
||||
The ValueTree will have been pre-checked to make sure that its type matches the type
|
||||
that this handler supports.
|
||||
|
||||
There's no need to set the new Component's ID to match that of the state - the builder
|
||||
will take care of that itself.
|
||||
*/
|
||||
virtual Component* addNewComponentFromState (const ValueTree& state, Component* parent) = 0;
|
||||
|
||||
/** This method must update an existing component from a new ValueTree state.
|
||||
|
||||
A component that has been created with addNewComponentFromState() may need to be updated
|
||||
if the ValueTree changes, so this method is used to do that. Your implementation must do
|
||||
whatever's necessary to update the component from the new state provided.
|
||||
|
||||
The ValueTree will have been pre-checked to make sure that its type matches the type
|
||||
that this handler supports, and the component will have been created by this type's
|
||||
addNewComponentFromState() method.
|
||||
*/
|
||||
virtual void updateComponentFromState (Component* component, const ValueTree& state) = 0;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class ComponentBuilder;
|
||||
ComponentBuilder* builder;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypeHandler)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a type handler that the builder can use when trying to load components.
|
||||
@see Drawable::registerDrawableTypeHandlers()
|
||||
*/
|
||||
void registerTypeHandler (TypeHandler* type);
|
||||
|
||||
/** Tries to find a registered type handler that can load a component from the given ValueTree. */
|
||||
TypeHandler* getHandlerForState (const ValueTree& state) const;
|
||||
|
||||
/** Returns the number of registered type handlers.
|
||||
@see getHandler, registerTypeHandler
|
||||
*/
|
||||
int getNumHandlers() const noexcept;
|
||||
|
||||
/** Returns one of the registered type handlers.
|
||||
@see getNumHandlers, registerTypeHandler
|
||||
*/
|
||||
TypeHandler* getHandler (int index) const noexcept;
|
||||
|
||||
/** Registers handlers for various standard juce components. */
|
||||
void registerStandardComponentTypes();
|
||||
|
||||
//==============================================================================
|
||||
/** This class is used when references to images need to be stored in ValueTrees.
|
||||
|
||||
An instance of an ImageProvider provides a mechanism for converting an Image to/from
|
||||
a reference, which may be a file, URL, ID string, or whatever system is appropriate in
|
||||
your app.
|
||||
|
||||
When you're loading components from a ValueTree that may need a way of loading images, you
|
||||
should call ComponentBuilder::setImageProvider() to supply a suitable provider before
|
||||
trying to load the component.
|
||||
|
||||
@see ComponentBuilder::setImageProvider()
|
||||
*/
|
||||
class JUCE_API ImageProvider
|
||||
{
|
||||
public:
|
||||
ImageProvider() = default;
|
||||
virtual ~ImageProvider() = default;
|
||||
|
||||
/** Retrieves the image associated with this identifier, which could be any
|
||||
kind of string, number, filename, etc.
|
||||
|
||||
The image that is returned will be owned by the caller, but it may come
|
||||
from the ImageCache.
|
||||
*/
|
||||
virtual Image getImageForIdentifier (const var& imageIdentifier) = 0;
|
||||
|
||||
/** Returns an identifier to be used to refer to a given image.
|
||||
This is used when a reference to an image is stored in a ValueTree.
|
||||
*/
|
||||
virtual var getIdentifierForImage (const Image& image) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Gives the builder an ImageProvider object that the type handlers can use when
|
||||
loading images from stored references.
|
||||
|
||||
The object that is passed in is not owned by the builder, so the caller must delete
|
||||
it when it is no longer needed, but not while the builder may still be using it. To
|
||||
clear the image provider, just call setImageProvider (nullptr).
|
||||
*/
|
||||
void setImageProvider (ImageProvider* newImageProvider) noexcept;
|
||||
|
||||
/** Returns the current image provider that this builder is using, or nullptr if none has been set. */
|
||||
ImageProvider* getImageProvider() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Updates the children of a parent component by updating them from the children of
|
||||
a given ValueTree.
|
||||
*/
|
||||
void updateChildComponents (Component& parent, const ValueTree& children);
|
||||
|
||||
/** An identifier for the property of the ValueTrees that is used to store a unique ID
|
||||
for that component.
|
||||
*/
|
||||
static const Identifier idProperty;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
OwnedArray<TypeHandler> types;
|
||||
std::unique_ptr<Component> component;
|
||||
ImageProvider* imageProvider;
|
||||
#if JUCE_DEBUG
|
||||
WeakReference<Component> componentRef;
|
||||
#endif
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override;
|
||||
void valueTreeParentChanged (ValueTree&) override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBuilder)
|
||||
};
|
||||
|
||||
} // namespace juce
|
142
deps/juce/modules/juce_gui_basics/layout/juce_ComponentMovementWatcher.cpp
vendored
Normal file
142
deps/juce/modules/juce_gui_basics/layout/juce_ComponentMovementWatcher.cpp
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ComponentMovementWatcher::ComponentMovementWatcher (Component* const comp)
|
||||
: component (comp),
|
||||
wasShowing (comp->isShowing())
|
||||
{
|
||||
jassert (component != nullptr); // can't use this with a null pointer..
|
||||
|
||||
component->addComponentListener (this);
|
||||
registerWithParentComps();
|
||||
}
|
||||
|
||||
ComponentMovementWatcher::~ComponentMovementWatcher()
|
||||
{
|
||||
if (component != nullptr)
|
||||
component->removeComponentListener (this);
|
||||
|
||||
unregister();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentMovementWatcher::componentParentHierarchyChanged (Component&)
|
||||
{
|
||||
if (component != nullptr && ! reentrant)
|
||||
{
|
||||
const ScopedValueSetter<bool> setter (reentrant, true);
|
||||
|
||||
auto* peer = component->getPeer();
|
||||
auto peerID = peer != nullptr ? peer->getUniqueID() : 0;
|
||||
|
||||
if (peerID != lastPeerID)
|
||||
{
|
||||
componentPeerChanged();
|
||||
|
||||
if (component == nullptr)
|
||||
return;
|
||||
|
||||
lastPeerID = peerID;
|
||||
}
|
||||
|
||||
unregister();
|
||||
registerWithParentComps();
|
||||
|
||||
componentMovedOrResized (*component, true, true);
|
||||
|
||||
if (component != nullptr)
|
||||
componentVisibilityChanged (*component);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentMovementWatcher::componentMovedOrResized (Component&, bool wasMoved, bool wasResized)
|
||||
{
|
||||
if (component != nullptr)
|
||||
{
|
||||
if (wasMoved)
|
||||
{
|
||||
Point<int> newPos;
|
||||
auto* top = component->getTopLevelComponent();
|
||||
|
||||
if (top != component)
|
||||
newPos = top->getLocalPoint (component, Point<int>());
|
||||
else
|
||||
newPos = top->getPosition();
|
||||
|
||||
wasMoved = lastBounds.getPosition() != newPos;
|
||||
lastBounds.setPosition (newPos);
|
||||
}
|
||||
|
||||
wasResized = (lastBounds.getWidth() != component->getWidth() || lastBounds.getHeight() != component->getHeight());
|
||||
lastBounds.setSize (component->getWidth(), component->getHeight());
|
||||
|
||||
if (wasMoved || wasResized)
|
||||
componentMovedOrResized (wasMoved, wasResized);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentMovementWatcher::componentBeingDeleted (Component& comp)
|
||||
{
|
||||
registeredParentComps.removeFirstMatchingValue (&comp);
|
||||
|
||||
if (component == &comp)
|
||||
unregister();
|
||||
}
|
||||
|
||||
void ComponentMovementWatcher::componentVisibilityChanged (Component&)
|
||||
{
|
||||
if (component != nullptr)
|
||||
{
|
||||
const bool isShowingNow = component->isShowing();
|
||||
|
||||
if (wasShowing != isShowingNow)
|
||||
{
|
||||
wasShowing = isShowingNow;
|
||||
componentVisibilityChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentMovementWatcher::registerWithParentComps()
|
||||
{
|
||||
for (auto* p = component->getParentComponent(); p != nullptr; p = p->getParentComponent())
|
||||
{
|
||||
p->addComponentListener (this);
|
||||
registeredParentComps.add (p);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentMovementWatcher::unregister()
|
||||
{
|
||||
for (auto* c : registeredParentComps)
|
||||
c->removeComponentListener (this);
|
||||
|
||||
registeredParentComps.clear();
|
||||
}
|
||||
|
||||
} // namespace juce
|
96
deps/juce/modules/juce_gui_basics/layout/juce_ComponentMovementWatcher.h
vendored
Normal file
96
deps/juce/modules/juce_gui_basics/layout/juce_ComponentMovementWatcher.h
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** An object that watches for any movement of a component or any of its parent components.
|
||||
|
||||
This makes it easy to check when a component is moved relative to its top-level
|
||||
peer window. The normal Component::moved() method is only called when a component
|
||||
moves relative to its immediate parent, and sometimes you want to know if any of
|
||||
components higher up the tree have moved (which of course will affect the overall
|
||||
position of all their sub-components).
|
||||
|
||||
It also includes a callback that lets you know when the top-level peer is changed.
|
||||
|
||||
This class is used by specialised components like WebBrowserComponent
|
||||
because they need to keep their custom windows in the right place and respond to
|
||||
changes in the peer.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ComponentMovementWatcher : public ComponentListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ComponentMovementWatcher to watch a given target component. */
|
||||
ComponentMovementWatcher (Component* componentToWatch);
|
||||
|
||||
/** Destructor. */
|
||||
~ComponentMovementWatcher() override;
|
||||
|
||||
//==============================================================================
|
||||
/** This callback happens when the component that is being watched is moved
|
||||
relative to its top-level peer window, or when it is resized. */
|
||||
virtual void componentMovedOrResized (bool wasMoved, bool wasResized) = 0;
|
||||
|
||||
/** This callback happens when the component's top-level peer is changed. */
|
||||
virtual void componentPeerChanged() = 0;
|
||||
|
||||
/** This callback happens when the component's visibility state changes, possibly due to
|
||||
one of its parents being made visible or invisible.
|
||||
*/
|
||||
virtual void componentVisibilityChanged() = 0;
|
||||
|
||||
/** Returns the component that's being watched. */
|
||||
Component* getComponent() const noexcept { return component.get(); }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void componentParentHierarchyChanged (Component&) override;
|
||||
/** @internal */
|
||||
void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
|
||||
/** @internal */
|
||||
void componentBeingDeleted (Component&) override;
|
||||
/** @internal */
|
||||
void componentVisibilityChanged (Component&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
WeakReference<Component> component;
|
||||
uint32 lastPeerID = 0;
|
||||
Array<Component*> registeredParentComps;
|
||||
bool reentrant = false, wasShowing;
|
||||
Rectangle<int> lastBounds;
|
||||
|
||||
void unregister();
|
||||
void registerWithParentComps();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentMovementWatcher)
|
||||
};
|
||||
|
||||
} // namespace juce
|
468
deps/juce/modules/juce_gui_basics/layout/juce_ConcertinaPanel.cpp
vendored
Normal file
468
deps/juce/modules/juce_gui_basics/layout/juce_ConcertinaPanel.cpp
vendored
Normal file
@ -0,0 +1,468 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct ConcertinaPanel::PanelSizes
|
||||
{
|
||||
struct Panel
|
||||
{
|
||||
Panel() = default;
|
||||
|
||||
Panel (int sz, int mn, int mx) noexcept
|
||||
: size (sz), minSize (mn), maxSize (mx) {}
|
||||
|
||||
int setSize (int newSize) noexcept
|
||||
{
|
||||
jassert (minSize <= maxSize);
|
||||
auto oldSize = size;
|
||||
size = jlimit (minSize, maxSize, newSize);
|
||||
return size - oldSize;
|
||||
}
|
||||
|
||||
int expand (int amount) noexcept
|
||||
{
|
||||
amount = jmin (amount, maxSize - size);
|
||||
size += amount;
|
||||
return amount;
|
||||
}
|
||||
|
||||
int reduce (int amount) noexcept
|
||||
{
|
||||
amount = jmin (amount, size - minSize);
|
||||
size -= amount;
|
||||
return amount;
|
||||
}
|
||||
|
||||
bool canExpand() const noexcept { return size < maxSize; }
|
||||
bool isMinimised() const noexcept { return size <= minSize; }
|
||||
|
||||
int size, minSize, maxSize;
|
||||
};
|
||||
|
||||
Array<Panel> sizes;
|
||||
|
||||
Panel& get (int index) noexcept { return sizes.getReference (index); }
|
||||
const Panel& get (int index) const noexcept { return sizes.getReference (index); }
|
||||
|
||||
PanelSizes withMovedPanel (int index, int targetPosition, int totalSpace) const
|
||||
{
|
||||
auto num = sizes.size();
|
||||
totalSpace = jmax (totalSpace, getMinimumSize (0, num));
|
||||
targetPosition = jmax (targetPosition, totalSpace - getMaximumSize (index, num));
|
||||
|
||||
PanelSizes newSizes (*this);
|
||||
newSizes.stretchRange (0, index, targetPosition - newSizes.getTotalSize (0, index), stretchLast);
|
||||
newSizes.stretchRange (index, num, totalSpace - newSizes.getTotalSize (0, index) - newSizes.getTotalSize (index, num), stretchFirst);
|
||||
return newSizes;
|
||||
}
|
||||
|
||||
PanelSizes fittedInto (int totalSpace) const
|
||||
{
|
||||
auto newSizes (*this);
|
||||
auto num = newSizes.sizes.size();
|
||||
totalSpace = jmax (totalSpace, getMinimumSize (0, num));
|
||||
newSizes.stretchRange (0, num, totalSpace - newSizes.getTotalSize (0, num), stretchAll);
|
||||
return newSizes;
|
||||
}
|
||||
|
||||
PanelSizes withResizedPanel (int index, int panelHeight, int totalSpace) const
|
||||
{
|
||||
PanelSizes newSizes (*this);
|
||||
|
||||
if (totalSpace <= 0)
|
||||
{
|
||||
newSizes.get(index).size = panelHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto num = sizes.size();
|
||||
auto minSize = getMinimumSize (0, num);
|
||||
totalSpace = jmax (totalSpace, minSize);
|
||||
|
||||
newSizes.get(index).setSize (panelHeight);
|
||||
newSizes.stretchRange (0, index, totalSpace - newSizes.getTotalSize (0, num), stretchLast);
|
||||
newSizes.stretchRange (index, num, totalSpace - newSizes.getTotalSize (0, num), stretchLast);
|
||||
newSizes = newSizes.fittedInto (totalSpace);
|
||||
}
|
||||
|
||||
return newSizes;
|
||||
}
|
||||
|
||||
private:
|
||||
enum ExpandMode
|
||||
{
|
||||
stretchAll,
|
||||
stretchFirst,
|
||||
stretchLast
|
||||
};
|
||||
|
||||
void growRangeFirst (int start, int end, int spaceDiff) noexcept
|
||||
{
|
||||
for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
|
||||
for (int i = start; i < end && spaceDiff > 0; ++i)
|
||||
spaceDiff -= get (i).expand (spaceDiff);
|
||||
}
|
||||
|
||||
void growRangeLast (int start, int end, int spaceDiff) noexcept
|
||||
{
|
||||
for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
|
||||
for (int i = end; --i >= start && spaceDiff > 0;)
|
||||
spaceDiff -= get (i).expand (spaceDiff);
|
||||
}
|
||||
|
||||
void growRangeAll (int start, int end, int spaceDiff) noexcept
|
||||
{
|
||||
Array<Panel*> expandableItems;
|
||||
|
||||
for (int i = start; i < end; ++i)
|
||||
if (get(i).canExpand() && ! get(i).isMinimised())
|
||||
expandableItems.add (& get(i));
|
||||
|
||||
for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
|
||||
for (int i = expandableItems.size(); --i >= 0 && spaceDiff > 0;)
|
||||
spaceDiff -= expandableItems.getUnchecked(i)->expand (spaceDiff / (i + 1));
|
||||
|
||||
growRangeLast (start, end, spaceDiff);
|
||||
}
|
||||
|
||||
void shrinkRangeFirst (int start, int end, int spaceDiff) noexcept
|
||||
{
|
||||
for (int i = start; i < end && spaceDiff > 0; ++i)
|
||||
spaceDiff -= get(i).reduce (spaceDiff);
|
||||
}
|
||||
|
||||
void shrinkRangeLast (int start, int end, int spaceDiff) noexcept
|
||||
{
|
||||
for (int i = end; --i >= start && spaceDiff > 0;)
|
||||
spaceDiff -= get(i).reduce (spaceDiff);
|
||||
}
|
||||
|
||||
void stretchRange (int start, int end, int amountToAdd, ExpandMode expandMode) noexcept
|
||||
{
|
||||
if (end > start)
|
||||
{
|
||||
if (amountToAdd > 0)
|
||||
{
|
||||
if (expandMode == stretchAll) growRangeAll (start, end, amountToAdd);
|
||||
else if (expandMode == stretchFirst) growRangeFirst (start, end, amountToAdd);
|
||||
else if (expandMode == stretchLast) growRangeLast (start, end, amountToAdd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (expandMode == stretchFirst) shrinkRangeFirst (start, end, -amountToAdd);
|
||||
else shrinkRangeLast (start, end, -amountToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getTotalSize (int start, int end) const noexcept
|
||||
{
|
||||
int tot = 0;
|
||||
while (start < end) tot += get (start++).size;
|
||||
return tot;
|
||||
}
|
||||
|
||||
int getMinimumSize (int start, int end) const noexcept
|
||||
{
|
||||
int tot = 0;
|
||||
while (start < end) tot += get (start++).minSize;
|
||||
return tot;
|
||||
}
|
||||
|
||||
int getMaximumSize (int start, int end) const noexcept
|
||||
{
|
||||
int tot = 0;
|
||||
|
||||
while (start < end)
|
||||
{
|
||||
auto mx = get (start++).maxSize;
|
||||
|
||||
if (mx > 0x100000)
|
||||
return mx;
|
||||
|
||||
tot += mx;
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ConcertinaPanel::PanelHolder : public Component
|
||||
{
|
||||
public:
|
||||
PanelHolder (Component* comp, bool takeOwnership)
|
||||
: component (comp, takeOwnership)
|
||||
{
|
||||
setRepaintsOnMouseActivity (true);
|
||||
setWantsKeyboardFocus (false);
|
||||
addAndMakeVisible (comp);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (customHeaderComponent == nullptr)
|
||||
{
|
||||
const Rectangle<int> area (getWidth(), getHeaderSize());
|
||||
g.reduceClipRegion (area);
|
||||
|
||||
getLookAndFeel().drawConcertinaPanelHeader (g, area, isMouseOver(), isMouseButtonDown(),
|
||||
getPanel(), *component);
|
||||
}
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
auto headerBounds = bounds.removeFromTop (getHeaderSize());
|
||||
|
||||
if (customHeaderComponent != nullptr)
|
||||
customHeaderComponent->setBounds (headerBounds);
|
||||
|
||||
component->setBounds (bounds);
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent&) override
|
||||
{
|
||||
mouseDownY = getY();
|
||||
dragStartSizes = getPanel().getFittedSizes();
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
{
|
||||
if (e.mouseWasDraggedSinceMouseDown())
|
||||
{
|
||||
auto& panel = getPanel();
|
||||
panel.setLayout (dragStartSizes.withMovedPanel (panel.holders.indexOf (this),
|
||||
mouseDownY + e.getDistanceFromDragStartY(),
|
||||
panel.getHeight()), false);
|
||||
}
|
||||
}
|
||||
|
||||
void mouseDoubleClick (const MouseEvent&) override
|
||||
{
|
||||
getPanel().panelHeaderDoubleClicked (component);
|
||||
}
|
||||
|
||||
void setCustomHeaderComponent (Component* headerComponent, bool shouldTakeOwnership)
|
||||
{
|
||||
customHeaderComponent.set (headerComponent, shouldTakeOwnership);
|
||||
|
||||
if (headerComponent != nullptr)
|
||||
{
|
||||
addAndMakeVisible (customHeaderComponent);
|
||||
customHeaderComponent->addMouseListener (this, false);
|
||||
}
|
||||
}
|
||||
|
||||
OptionalScopedPointer<Component> component;
|
||||
|
||||
private:
|
||||
PanelSizes dragStartSizes;
|
||||
int mouseDownY;
|
||||
OptionalScopedPointer<Component> customHeaderComponent;
|
||||
|
||||
int getHeaderSize() const noexcept
|
||||
{
|
||||
ConcertinaPanel& panel = getPanel();
|
||||
auto ourIndex = panel.holders.indexOf (this);
|
||||
return panel.currentSizes->get(ourIndex).minSize;
|
||||
}
|
||||
|
||||
ConcertinaPanel& getPanel() const
|
||||
{
|
||||
auto panel = dynamic_cast<ConcertinaPanel*> (getParentComponent());
|
||||
jassert (panel != nullptr);
|
||||
return *panel;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PanelHolder)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ConcertinaPanel::ConcertinaPanel()
|
||||
: currentSizes (new PanelSizes()),
|
||||
headerHeight (20)
|
||||
{
|
||||
}
|
||||
|
||||
ConcertinaPanel::~ConcertinaPanel() {}
|
||||
|
||||
int ConcertinaPanel::getNumPanels() const noexcept
|
||||
{
|
||||
return holders.size();
|
||||
}
|
||||
|
||||
Component* ConcertinaPanel::getPanel (int index) const noexcept
|
||||
{
|
||||
if (PanelHolder* h = holders[index])
|
||||
return h->component;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ConcertinaPanel::addPanel (int insertIndex, Component* component, bool takeOwnership)
|
||||
{
|
||||
jassert (component != nullptr); // can't use a null pointer here!
|
||||
jassert (indexOfComp (component) < 0); // You can't add the same component more than once!
|
||||
|
||||
auto holder = new PanelHolder (component, takeOwnership);
|
||||
holders.insert (insertIndex, holder);
|
||||
currentSizes->sizes.insert (insertIndex, PanelSizes::Panel (headerHeight, headerHeight, std::numeric_limits<int>::max()));
|
||||
addAndMakeVisible (holder);
|
||||
resized();
|
||||
}
|
||||
|
||||
void ConcertinaPanel::removePanel (Component* component)
|
||||
{
|
||||
auto index = indexOfComp (component);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
currentSizes->sizes.remove (index);
|
||||
holders.remove (index);
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConcertinaPanel::setPanelSize (Component* panelComponent, int height, bool animate)
|
||||
{
|
||||
auto index = indexOfComp (panelComponent);
|
||||
jassert (index >= 0); // The specified component doesn't seem to have been added!
|
||||
|
||||
height += currentSizes->get(index).minSize;
|
||||
auto oldSize = currentSizes->get(index).size;
|
||||
setLayout (currentSizes->withResizedPanel (index, height, getHeight()), animate);
|
||||
return oldSize != currentSizes->get(index).size;
|
||||
}
|
||||
|
||||
bool ConcertinaPanel::expandPanelFully (Component* component, bool animate)
|
||||
{
|
||||
return setPanelSize (component, getHeight(), animate);
|
||||
}
|
||||
|
||||
void ConcertinaPanel::setMaximumPanelSize (Component* component, int maximumSize)
|
||||
{
|
||||
auto index = indexOfComp (component);
|
||||
jassert (index >= 0); // The specified component doesn't seem to have been added!
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
currentSizes->get(index).maxSize = currentSizes->get(index).minSize + maximumSize;
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
void ConcertinaPanel::setPanelHeaderSize (Component* component, int headerSize)
|
||||
{
|
||||
auto index = indexOfComp (component);
|
||||
jassert (index >= 0); // The specified component doesn't seem to have been added!
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
auto oldMin = currentSizes->get (index).minSize;
|
||||
|
||||
currentSizes->get (index).minSize = headerSize;
|
||||
currentSizes->get (index).size += headerSize - oldMin;
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
void ConcertinaPanel::setCustomPanelHeader (Component* component, Component* customComponent, bool takeOwnership)
|
||||
{
|
||||
OptionalScopedPointer<Component> optional (customComponent, takeOwnership);
|
||||
|
||||
auto index = indexOfComp (component);
|
||||
jassert (index >= 0); // The specified component doesn't seem to have been added!
|
||||
|
||||
if (index >= 0)
|
||||
holders.getUnchecked (index)->setCustomHeaderComponent (optional.release(), takeOwnership);
|
||||
}
|
||||
|
||||
void ConcertinaPanel::resized()
|
||||
{
|
||||
applyLayout (getFittedSizes(), false);
|
||||
}
|
||||
|
||||
int ConcertinaPanel::indexOfComp (Component* comp) const noexcept
|
||||
{
|
||||
for (int i = 0; i < holders.size(); ++i)
|
||||
if (holders.getUnchecked(i)->component == comp)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ConcertinaPanel::PanelSizes ConcertinaPanel::getFittedSizes() const
|
||||
{
|
||||
return currentSizes->fittedInto (getHeight());
|
||||
}
|
||||
|
||||
void ConcertinaPanel::applyLayout (const PanelSizes& sizes, bool animate)
|
||||
{
|
||||
if (! animate)
|
||||
animator.cancelAllAnimations (false);
|
||||
|
||||
const int animationDuration = 150;
|
||||
auto w = getWidth();
|
||||
int y = 0;
|
||||
|
||||
for (int i = 0; i < holders.size(); ++i)
|
||||
{
|
||||
PanelHolder& p = *holders.getUnchecked (i);
|
||||
|
||||
auto h = sizes.get (i).size;
|
||||
const Rectangle<int> pos (0, y, w, h);
|
||||
|
||||
if (animate)
|
||||
animator.animateComponent (&p, pos, 1.0f, animationDuration, false, 1.0, 1.0);
|
||||
else
|
||||
p.setBounds (pos);
|
||||
|
||||
y += h;
|
||||
}
|
||||
}
|
||||
|
||||
void ConcertinaPanel::setLayout (const PanelSizes& sizes, bool animate)
|
||||
{
|
||||
*currentSizes = sizes;
|
||||
applyLayout (getFittedSizes(), animate);
|
||||
}
|
||||
|
||||
void ConcertinaPanel::panelHeaderDoubleClicked (Component* component)
|
||||
{
|
||||
if (! expandPanelFully (component, true))
|
||||
setPanelSize (component, 0, true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> ConcertinaPanel::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
|
||||
}
|
||||
|
||||
} // namespace juce
|
142
deps/juce/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h
vendored
Normal file
142
deps/juce/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A panel which holds a vertical stack of components which can be expanded
|
||||
and contracted.
|
||||
|
||||
Each section has its own header bar which can be dragged up and down
|
||||
to resize it, or double-clicked to fully expand that section.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ConcertinaPanel : public Component
|
||||
{
|
||||
public:
|
||||
/** Creates an empty concertina panel.
|
||||
You can call addPanel() to add some components to it.
|
||||
*/
|
||||
ConcertinaPanel();
|
||||
|
||||
/** Destructor. */
|
||||
~ConcertinaPanel() override;
|
||||
|
||||
/** Adds a component to the panel.
|
||||
@param insertIndex the index at which this component will be inserted, or
|
||||
-1 to append it to the end of the list.
|
||||
@param component the component that will be shown
|
||||
@param takeOwnership if true, then the ConcertinaPanel will take ownership
|
||||
of the content component, and will delete it later when
|
||||
it's no longer needed. If false, it won't delete it, and
|
||||
you must make sure it doesn't get deleted while in use.
|
||||
*/
|
||||
void addPanel (int insertIndex, Component* component, bool takeOwnership);
|
||||
|
||||
/** Removes one of the panels.
|
||||
If the takeOwnership flag was set when the panel was added, then
|
||||
this will also delete the component.
|
||||
*/
|
||||
void removePanel (Component* panelComponent);
|
||||
|
||||
/** Returns the number of panels.
|
||||
@see getPanel
|
||||
*/
|
||||
int getNumPanels() const noexcept;
|
||||
|
||||
/** Returns one of the panels.
|
||||
@see getNumPanels()
|
||||
*/
|
||||
Component* getPanel (int index) const noexcept;
|
||||
|
||||
/** Resizes one of the panels.
|
||||
The panelComponent must point to a valid panel component.
|
||||
If animate is true, the panels will be animated into their new positions;
|
||||
if false, they will just be immediately resized.
|
||||
*/
|
||||
bool setPanelSize (Component* panelComponent, int newHeight, bool animate);
|
||||
|
||||
/** Attempts to make one of the panels full-height.
|
||||
The panelComponent must point to a valid panel component.
|
||||
If this component has had a maximum size set, then it will be
|
||||
expanded to that size. Otherwise, it'll fill as much of the total
|
||||
space as possible.
|
||||
*/
|
||||
bool expandPanelFully (Component* panelComponent, bool animate);
|
||||
|
||||
/** Sets a maximum size for one of the panels. */
|
||||
void setMaximumPanelSize (Component* panelComponent, int maximumSize);
|
||||
|
||||
/** Sets the height of the header section for one of the panels. */
|
||||
void setPanelHeaderSize (Component* panelComponent, int headerSize);
|
||||
|
||||
/** Sets a custom header Component for one of the panels.
|
||||
|
||||
@param panelComponent the panel component to add the custom header to.
|
||||
@param customHeaderComponent the custom component to use for the panel header.
|
||||
This can be nullptr to clear the custom header component
|
||||
and just use the standard LookAndFeel panel.
|
||||
@param takeOwnership if true, then the PanelHolder will take ownership
|
||||
of the custom header component, and will delete it later when
|
||||
it's no longer needed. If false, it won't delete it, and
|
||||
you must make sure it doesn't get deleted while in use.
|
||||
*/
|
||||
void setCustomPanelHeader (Component* panelComponent, Component* customHeaderComponent, bool takeOwnership);
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes. */
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual void drawConcertinaPanelHeader (Graphics&, const Rectangle<int>& area,
|
||||
bool isMouseOver, bool isMouseDown,
|
||||
ConcertinaPanel&, Component&) = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void resized() override;
|
||||
|
||||
class PanelHolder;
|
||||
struct PanelSizes;
|
||||
std::unique_ptr<PanelSizes> currentSizes;
|
||||
OwnedArray<PanelHolder> holders;
|
||||
ComponentAnimator animator;
|
||||
int headerHeight;
|
||||
|
||||
int indexOfComp (Component*) const noexcept;
|
||||
PanelSizes getFittedSizes() const;
|
||||
void applyLayout (const PanelSizes&, bool animate);
|
||||
void setLayout (const PanelSizes&, bool animate);
|
||||
void panelHeaderDoubleClicked (Component*);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaPanel)
|
||||
};
|
||||
|
||||
} // namespace juce
|
859
deps/juce/modules/juce_gui_basics/layout/juce_FlexBox.cpp
vendored
Normal file
859
deps/juce/modules/juce_gui_basics/layout/juce_FlexBox.cpp
vendored
Normal file
@ -0,0 +1,859 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct FlexBoxLayoutCalculation
|
||||
{
|
||||
using Coord = double;
|
||||
|
||||
FlexBoxLayoutCalculation (FlexBox& fb, Coord w, Coord h)
|
||||
: owner (fb), parentWidth (w), parentHeight (h), numItems (owner.items.size()),
|
||||
isRowDirection (fb.flexDirection == FlexBox::Direction::row
|
||||
|| fb.flexDirection == FlexBox::Direction::rowReverse),
|
||||
containerLineLength (isRowDirection ? parentWidth : parentHeight)
|
||||
{
|
||||
lineItems.calloc (numItems * numItems);
|
||||
lineInfo.calloc (numItems);
|
||||
}
|
||||
|
||||
struct ItemWithState
|
||||
{
|
||||
ItemWithState (FlexItem& source) noexcept : item (&source) {}
|
||||
|
||||
FlexItem* item;
|
||||
Coord lockedWidth = 0, lockedHeight = 0;
|
||||
Coord lockedMarginLeft = 0, lockedMarginRight = 0, lockedMarginTop = 0, lockedMarginBottom = 0;
|
||||
Coord preferredWidth = 0, preferredHeight = 0;
|
||||
bool locked = false;
|
||||
|
||||
void resetItemLockedSize() noexcept
|
||||
{
|
||||
lockedWidth = preferredWidth;
|
||||
lockedHeight = preferredHeight;
|
||||
lockedMarginLeft = getValueOrZeroIfAuto (item->margin.left);
|
||||
lockedMarginRight = getValueOrZeroIfAuto (item->margin.right);
|
||||
lockedMarginTop = getValueOrZeroIfAuto (item->margin.top);
|
||||
lockedMarginBottom = getValueOrZeroIfAuto (item->margin.bottom);
|
||||
}
|
||||
|
||||
void setWidthChecked (Coord newWidth) noexcept
|
||||
{
|
||||
if (isAssigned (item->maxWidth)) newWidth = jmin (newWidth, static_cast<Coord> (item->maxWidth));
|
||||
if (isAssigned (item->minWidth)) newWidth = jmax (newWidth, static_cast<Coord> (item->minWidth));
|
||||
|
||||
lockedWidth = newWidth;
|
||||
}
|
||||
|
||||
void setHeightChecked (Coord newHeight) noexcept
|
||||
{
|
||||
if (isAssigned (item->maxHeight)) newHeight = jmin (newHeight, (Coord) item->maxHeight);
|
||||
if (isAssigned (item->minHeight)) newHeight = jmax (newHeight, (Coord) item->minHeight);
|
||||
|
||||
lockedHeight = newHeight;
|
||||
}
|
||||
};
|
||||
|
||||
struct RowInfo
|
||||
{
|
||||
int numItems;
|
||||
Coord crossSize, lineY, totalLength;
|
||||
};
|
||||
|
||||
FlexBox& owner;
|
||||
const Coord parentWidth, parentHeight;
|
||||
const int numItems;
|
||||
const bool isRowDirection;
|
||||
const Coord containerLineLength;
|
||||
|
||||
int numberOfRows = 1;
|
||||
Coord containerCrossLength = 0;
|
||||
|
||||
HeapBlock<ItemWithState*> lineItems;
|
||||
HeapBlock<RowInfo> lineInfo;
|
||||
Array<ItemWithState> itemStates;
|
||||
|
||||
ItemWithState& getItem (int x, int y) const noexcept { return *lineItems[y * numItems + x]; }
|
||||
|
||||
static bool isAuto (Coord value) noexcept { return value == FlexItem::autoValue; }
|
||||
static bool isAssigned (Coord value) noexcept { return value != FlexItem::notAssigned; }
|
||||
static Coord getValueOrZeroIfAuto (Coord value) noexcept { return isAuto (value) ? Coord() : value; }
|
||||
|
||||
//==============================================================================
|
||||
void createStates()
|
||||
{
|
||||
itemStates.ensureStorageAllocated (numItems);
|
||||
|
||||
for (auto& item : owner.items)
|
||||
itemStates.add (item);
|
||||
|
||||
std::stable_sort (itemStates.begin(), itemStates.end(),
|
||||
[] (const ItemWithState& i1, const ItemWithState& i2) { return i1.item->order < i2.item->order; });
|
||||
|
||||
for (auto& item : itemStates)
|
||||
{
|
||||
item.preferredWidth = getPreferredWidth (item);
|
||||
item.preferredHeight = getPreferredHeight (item);
|
||||
}
|
||||
}
|
||||
|
||||
void initialiseItems() noexcept
|
||||
{
|
||||
if (owner.flexWrap == FlexBox::Wrap::noWrap) // for single-line, all items go in line 1
|
||||
{
|
||||
lineInfo[0].numItems = numItems;
|
||||
int i = 0;
|
||||
|
||||
for (auto& item : itemStates)
|
||||
{
|
||||
item.resetItemLockedSize();
|
||||
lineItems[i++] = &item;
|
||||
}
|
||||
}
|
||||
else // if multi-line, group the flexbox items into multiple lines
|
||||
{
|
||||
auto currentLength = containerLineLength;
|
||||
int column = 0, row = 0;
|
||||
bool firstRow = true;
|
||||
|
||||
for (auto& item : itemStates)
|
||||
{
|
||||
item.resetItemLockedSize();
|
||||
|
||||
const auto flexitemLength = getItemLength (item);
|
||||
|
||||
if (flexitemLength > currentLength)
|
||||
{
|
||||
if (! firstRow)
|
||||
row++;
|
||||
|
||||
if (row >= numItems)
|
||||
break;
|
||||
|
||||
column = 0;
|
||||
currentLength = containerLineLength;
|
||||
numberOfRows = jmax (numberOfRows, row + 1);
|
||||
}
|
||||
|
||||
currentLength -= flexitemLength;
|
||||
lineItems[row * numItems + column] = &item;
|
||||
++column;
|
||||
lineInfo[row].numItems = jmax (lineInfo[row].numItems, column);
|
||||
firstRow = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resolveFlexibleLengths() noexcept
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
resetRowItems (row);
|
||||
|
||||
for (int maxLoops = numItems; --maxLoops >= 0;)
|
||||
{
|
||||
resetUnlockedRowItems (row);
|
||||
|
||||
if (layoutRowItems (row))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resolveAutoMarginsOnMainAxis() noexcept
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
Coord allFlexGrow = 0;
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
const auto remainingLength = containerLineLength - lineInfo[row].totalLength;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (isRowDirection)
|
||||
{
|
||||
if (isAuto (item.item->margin.left)) ++allFlexGrow;
|
||||
if (isAuto (item.item->margin.right)) ++allFlexGrow;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isAuto (item.item->margin.top)) ++allFlexGrow;
|
||||
if (isAuto (item.item->margin.bottom)) ++allFlexGrow;
|
||||
}
|
||||
}
|
||||
|
||||
auto changeUnitWidth = remainingLength / allFlexGrow;
|
||||
|
||||
if (changeUnitWidth > 0)
|
||||
{
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (isRowDirection)
|
||||
{
|
||||
if (isAuto (item.item->margin.left)) item.lockedMarginLeft = changeUnitWidth;
|
||||
if (isAuto (item.item->margin.right)) item.lockedMarginRight = changeUnitWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isAuto (item.item->margin.top)) item.lockedMarginTop = changeUnitWidth;
|
||||
if (isAuto (item.item->margin.bottom)) item.lockedMarginBottom = changeUnitWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calculateCrossSizesByLine() noexcept
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
Coord maxSize = 0;
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
maxSize = jmax (maxSize, isRowDirection ? item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom
|
||||
: item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight);
|
||||
}
|
||||
|
||||
lineInfo[row].crossSize = maxSize;
|
||||
}
|
||||
}
|
||||
|
||||
void calculateCrossSizeOfAllItems() noexcept
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (isAssigned (item.item->maxHeight) && item.lockedHeight > item.item->maxHeight)
|
||||
item.lockedHeight = item.item->maxHeight;
|
||||
|
||||
if (isAssigned (item.item->maxWidth) && item.lockedWidth > item.item->maxWidth)
|
||||
item.lockedWidth = item.item->maxWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void alignLinesPerAlignContent() noexcept
|
||||
{
|
||||
containerCrossLength = isRowDirection ? parentHeight : parentWidth;
|
||||
|
||||
if (owner.alignContent == FlexBox::AlignContent::flexStart)
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
for (int row2 = row; row2 < numberOfRows; ++row2)
|
||||
lineInfo[row].lineY = row == 0 ? 0 : lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
|
||||
}
|
||||
else if (owner.alignContent == FlexBox::AlignContent::flexEnd)
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
Coord crossHeights = 0;
|
||||
|
||||
for (int row2 = row; row2 < numberOfRows; ++row2)
|
||||
crossHeights += lineInfo[row2].crossSize;
|
||||
|
||||
lineInfo[row].lineY = containerCrossLength - crossHeights;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Coord totalHeight = 0;
|
||||
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
totalHeight += lineInfo[row].crossSize;
|
||||
|
||||
if (owner.alignContent == FlexBox::AlignContent::stretch)
|
||||
{
|
||||
const auto difference = jmax (Coord(), (containerCrossLength - totalHeight) / numberOfRows);
|
||||
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
lineInfo[row].crossSize += difference;
|
||||
lineInfo[row].lineY = row == 0 ? 0 : lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
|
||||
}
|
||||
}
|
||||
else if (owner.alignContent == FlexBox::AlignContent::center)
|
||||
{
|
||||
const auto additionalength = (containerCrossLength - totalHeight) / 2;
|
||||
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
lineInfo[row].lineY = row == 0 ? additionalength : lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
|
||||
}
|
||||
else if (owner.alignContent == FlexBox::AlignContent::spaceBetween)
|
||||
{
|
||||
const auto additionalength = numberOfRows <= 1 ? Coord() : jmax (Coord(), (containerCrossLength - totalHeight)
|
||||
/ static_cast<Coord> (numberOfRows - 1));
|
||||
lineInfo[0].lineY = 0;
|
||||
|
||||
for (int row = 1; row < numberOfRows; ++row)
|
||||
lineInfo[row].lineY += additionalength + lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
|
||||
}
|
||||
else if (owner.alignContent == FlexBox::AlignContent::spaceAround)
|
||||
{
|
||||
const auto additionalength = numberOfRows <= 1 ? Coord() : jmax (Coord(), (containerCrossLength - totalHeight)
|
||||
/ static_cast<Coord> (2 + (2 * (numberOfRows - 1))));
|
||||
|
||||
lineInfo[0].lineY = additionalength;
|
||||
|
||||
for (int row = 1; row < numberOfRows; ++row)
|
||||
lineInfo[row].lineY += (2 * additionalength) + lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resolveAutoMarginsOnCrossAxis() noexcept
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
const auto crossSizeForLine = lineInfo[row].crossSize;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (isRowDirection)
|
||||
{
|
||||
if (isAuto (item.item->margin.top) && isAuto (item.item->margin.bottom))
|
||||
item.lockedMarginTop = (crossSizeForLine - item.lockedHeight) / 2;
|
||||
else if (isAuto (item.item->margin.top))
|
||||
item.lockedMarginTop = crossSizeForLine - item.lockedHeight - item.item->margin.bottom;
|
||||
}
|
||||
else if (isAuto (item.item->margin.left) && isAuto (item.item->margin.right))
|
||||
{
|
||||
item.lockedMarginLeft = jmax (Coord(), (crossSizeForLine - item.lockedWidth) / 2);
|
||||
}
|
||||
else if (isAuto (item.item->margin.top))
|
||||
{
|
||||
item.lockedMarginLeft = jmax (Coord(), crossSizeForLine - item.lockedHeight - item.item->margin.bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void alignItemsInCrossAxisInLinesPerAlignItems() noexcept
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
const auto lineSize = lineInfo[row].crossSize;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (item.item->alignSelf == FlexItem::AlignSelf::autoAlign)
|
||||
{
|
||||
if (owner.alignItems == FlexBox::AlignItems::stretch)
|
||||
{
|
||||
item.lockedMarginTop = item.item->margin.top;
|
||||
|
||||
if (isRowDirection)
|
||||
item.setHeightChecked (lineSize - item.item->margin.top - item.item->margin.bottom);
|
||||
else
|
||||
item.setWidthChecked (lineSize - item.item->margin.left - item.item->margin.right);
|
||||
}
|
||||
else if (owner.alignItems == FlexBox::AlignItems::flexStart)
|
||||
{
|
||||
item.lockedMarginTop = item.item->margin.top;
|
||||
}
|
||||
else if (owner.alignItems == FlexBox::AlignItems::flexEnd)
|
||||
{
|
||||
if (isRowDirection)
|
||||
item.lockedMarginTop = lineSize - item.lockedHeight - item.item->margin.bottom;
|
||||
else
|
||||
item.lockedMarginLeft = lineSize - item.lockedWidth - item.item->margin.right;
|
||||
}
|
||||
else if (owner.alignItems == FlexBox::AlignItems::center)
|
||||
{
|
||||
if (isRowDirection)
|
||||
item.lockedMarginTop = (lineSize - item.lockedHeight - item.item->margin.top - item.item->margin.bottom) / 2;
|
||||
else
|
||||
item.lockedMarginLeft = (lineSize - item.lockedWidth - item.item->margin.left - item.item->margin.right) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void alignLinesPerAlignSelf() noexcept
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
const auto lineSize = lineInfo[row].crossSize;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (! isAuto (item.item->margin.top))
|
||||
{
|
||||
if (item.item->alignSelf == FlexItem::AlignSelf::flexStart)
|
||||
{
|
||||
if (isRowDirection)
|
||||
item.lockedMarginTop = item.item->margin.top;
|
||||
else
|
||||
item.lockedMarginLeft = item.item->margin.left;
|
||||
}
|
||||
else if (item.item->alignSelf == FlexItem::AlignSelf::flexEnd)
|
||||
{
|
||||
if (isRowDirection)
|
||||
item.lockedMarginTop = lineSize - item.lockedHeight - item.item->margin.bottom;
|
||||
else
|
||||
item.lockedMarginLeft = lineSize - item.lockedWidth - item.item->margin.right;
|
||||
}
|
||||
else if (item.item->alignSelf == FlexItem::AlignSelf::center)
|
||||
{
|
||||
if (isRowDirection)
|
||||
item.lockedMarginTop = item.item->margin.top + (lineSize - item.lockedHeight - item.item->margin.top - item.item->margin.bottom) / 2;
|
||||
else
|
||||
item.lockedMarginLeft = item.item->margin.left + (lineSize - item.lockedWidth - item.item->margin.left - item.item->margin.right) / 2;
|
||||
}
|
||||
else if (item.item->alignSelf == FlexItem::AlignSelf::stretch)
|
||||
{
|
||||
item.lockedMarginTop = item.item->margin.top;
|
||||
item.lockedMarginLeft = item.item->margin.left;
|
||||
|
||||
if (isRowDirection)
|
||||
item.setHeightChecked (isAssigned (item.item->height) ? getPreferredHeight (item)
|
||||
: lineSize - item.item->margin.top - item.item->margin.bottom);
|
||||
else
|
||||
item.setWidthChecked (isAssigned (item.item->width) ? getPreferredWidth (item)
|
||||
: lineSize - item.item->margin.left - item.item->margin.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void alignItemsByJustifyContent() noexcept
|
||||
{
|
||||
Coord additionalMarginRight = 0, additionalMarginLeft = 0;
|
||||
|
||||
recalculateTotalItemLengthPerLineArray();
|
||||
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
Coord x = 0;
|
||||
|
||||
if (owner.justifyContent == FlexBox::JustifyContent::flexEnd)
|
||||
{
|
||||
x = containerLineLength - lineInfo[row].totalLength;
|
||||
}
|
||||
else if (owner.justifyContent == FlexBox::JustifyContent::center)
|
||||
{
|
||||
x = (containerLineLength - lineInfo[row].totalLength) / 2;
|
||||
}
|
||||
else if (owner.justifyContent == FlexBox::JustifyContent::spaceBetween)
|
||||
{
|
||||
additionalMarginRight
|
||||
= jmax (Coord(), (containerLineLength - lineInfo[row].totalLength) / jmax (1, numColumns - 1));
|
||||
}
|
||||
else if (owner.justifyContent == FlexBox::JustifyContent::spaceAround)
|
||||
{
|
||||
additionalMarginLeft = additionalMarginRight
|
||||
= jmax (Coord(), (containerLineLength - lineInfo[row].totalLength) / jmax (1, 2 * numColumns));
|
||||
}
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (isRowDirection)
|
||||
{
|
||||
item.lockedMarginLeft += additionalMarginLeft;
|
||||
item.lockedMarginRight += additionalMarginRight;
|
||||
item.item->currentBounds.setPosition ((float) (x + item.lockedMarginLeft), (float) item.lockedMarginTop);
|
||||
x += item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.lockedMarginTop += additionalMarginLeft;
|
||||
item.lockedMarginBottom += additionalMarginRight;
|
||||
item.item->currentBounds.setPosition ((float) item.lockedMarginLeft, (float) (x + item.lockedMarginTop));
|
||||
x += item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void layoutAllItems() noexcept
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
const auto lineY = lineInfo[row].lineY;
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (isRowDirection)
|
||||
item.item->currentBounds.setY ((float) (lineY + item.lockedMarginTop));
|
||||
else
|
||||
item.item->currentBounds.setX ((float) (lineY + item.lockedMarginLeft));
|
||||
|
||||
item.item->currentBounds.setSize ((float) item.lockedWidth,
|
||||
(float) item.lockedHeight);
|
||||
}
|
||||
}
|
||||
|
||||
reverseLocations();
|
||||
reverseWrap();
|
||||
}
|
||||
|
||||
private:
|
||||
void resetRowItems (const int row) noexcept
|
||||
{
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
resetItem (getItem (column, row));
|
||||
}
|
||||
|
||||
void resetUnlockedRowItems (const int row) noexcept
|
||||
{
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (! item.locked)
|
||||
resetItem (item);
|
||||
}
|
||||
}
|
||||
|
||||
void resetItem (ItemWithState& item) noexcept
|
||||
{
|
||||
item.locked = false;
|
||||
item.lockedWidth = getPreferredWidth (item);
|
||||
item.lockedHeight = getPreferredHeight (item);
|
||||
}
|
||||
|
||||
bool layoutRowItems (const int row) noexcept
|
||||
{
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
auto flexContainerLength = containerLineLength;
|
||||
Coord totalItemsLength = 0, totalFlexGrow = 0, totalFlexShrink = 0;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
const auto& item = getItem (column, row);
|
||||
|
||||
if (item.locked)
|
||||
{
|
||||
flexContainerLength -= getItemLength (item);
|
||||
}
|
||||
else
|
||||
{
|
||||
totalItemsLength += getItemLength (item);
|
||||
totalFlexGrow += item.item->flexGrow;
|
||||
totalFlexShrink += item.item->flexShrink;
|
||||
}
|
||||
}
|
||||
|
||||
Coord changeUnit = 0;
|
||||
const auto difference = flexContainerLength - totalItemsLength;
|
||||
const bool positiveFlexibility = difference > 0;
|
||||
|
||||
if (positiveFlexibility)
|
||||
{
|
||||
if (totalFlexGrow != 0.0)
|
||||
changeUnit = difference / totalFlexGrow;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (totalFlexShrink != 0.0)
|
||||
changeUnit = difference / totalFlexShrink;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
auto& item = getItem (column, row);
|
||||
|
||||
if (! item.locked)
|
||||
if (! addToItemLength (item, (positiveFlexibility ? item.item->flexGrow
|
||||
: item.item->flexShrink) * changeUnit, row))
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void recalculateTotalItemLengthPerLineArray() noexcept
|
||||
{
|
||||
for (int row = 0; row < numberOfRows; ++row)
|
||||
{
|
||||
lineInfo[row].totalLength = 0;
|
||||
const auto numColumns = lineInfo[row].numItems;
|
||||
|
||||
for (int column = 0; column < numColumns; ++column)
|
||||
{
|
||||
const auto& item = getItem (column, row);
|
||||
|
||||
lineInfo[row].totalLength += isRowDirection ? item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight
|
||||
: item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reverseLocations() noexcept
|
||||
{
|
||||
if (owner.flexDirection == FlexBox::Direction::rowReverse)
|
||||
{
|
||||
for (auto& item : owner.items)
|
||||
item.currentBounds.setX ((float) (containerLineLength - item.currentBounds.getRight()));
|
||||
}
|
||||
else if (owner.flexDirection == FlexBox::Direction::columnReverse)
|
||||
{
|
||||
for (auto& item : owner.items)
|
||||
item.currentBounds.setY ((float) (containerLineLength - item.currentBounds.getBottom()));
|
||||
}
|
||||
}
|
||||
|
||||
void reverseWrap() noexcept
|
||||
{
|
||||
if (owner.flexWrap == FlexBox::Wrap::wrapReverse)
|
||||
{
|
||||
if (isRowDirection)
|
||||
{
|
||||
for (auto& item : owner.items)
|
||||
item.currentBounds.setY ((float) (containerCrossLength - item.currentBounds.getBottom()));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& item : owner.items)
|
||||
item.currentBounds.setX ((float) (containerCrossLength - item.currentBounds.getRight()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Coord getItemLength (const ItemWithState& item) const noexcept
|
||||
{
|
||||
return isRowDirection ? item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight
|
||||
: item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom;
|
||||
}
|
||||
|
||||
Coord getItemCrossSize (const ItemWithState& item) const noexcept
|
||||
{
|
||||
return isRowDirection ? item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom
|
||||
: item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight;
|
||||
}
|
||||
|
||||
bool addToItemLength (ItemWithState& item, const Coord length, int row) const noexcept
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
if (isRowDirection)
|
||||
{
|
||||
const auto prefWidth = getPreferredWidth (item);
|
||||
|
||||
if (isAssigned (item.item->maxWidth) && item.item->maxWidth < prefWidth + length)
|
||||
{
|
||||
item.lockedWidth = item.item->maxWidth;
|
||||
item.locked = true;
|
||||
}
|
||||
else if (isAssigned (prefWidth) && item.item->minWidth > prefWidth + length)
|
||||
{
|
||||
item.lockedWidth = item.item->minWidth;
|
||||
item.locked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = true;
|
||||
item.lockedWidth = prefWidth + length;
|
||||
}
|
||||
|
||||
lineInfo[row].totalLength += item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prefHeight = getPreferredHeight (item);
|
||||
|
||||
if (isAssigned (item.item->maxHeight) && item.item->maxHeight < prefHeight + length)
|
||||
{
|
||||
item.lockedHeight = item.item->maxHeight;
|
||||
item.locked = true;
|
||||
}
|
||||
else if (isAssigned (prefHeight) && item.item->minHeight > prefHeight + length)
|
||||
{
|
||||
item.lockedHeight = item.item->minHeight;
|
||||
item.locked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = true;
|
||||
item.lockedHeight = prefHeight + length;
|
||||
}
|
||||
|
||||
lineInfo[row].totalLength += item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
Coord getPreferredWidth (const ItemWithState& itemWithState) const noexcept
|
||||
{
|
||||
const auto& item = *itemWithState.item;
|
||||
auto preferredWidth = (item.flexBasis > 0 && isRowDirection)
|
||||
? item.flexBasis
|
||||
: (isAssigned (item.width) ? item.width : item.minWidth);
|
||||
|
||||
if (isAssigned (item.minWidth) && preferredWidth < item.minWidth) return item.minWidth;
|
||||
if (isAssigned (item.maxWidth) && preferredWidth > item.maxWidth) return item.maxWidth;
|
||||
|
||||
return preferredWidth;
|
||||
}
|
||||
|
||||
Coord getPreferredHeight (const ItemWithState& itemWithState) const noexcept
|
||||
{
|
||||
const auto& item = *itemWithState.item;
|
||||
auto preferredHeight = (item.flexBasis > 0 && ! isRowDirection)
|
||||
? item.flexBasis
|
||||
: (isAssigned (item.height) ? item.height : item.minHeight);
|
||||
|
||||
if (isAssigned (item.minHeight) && preferredHeight < item.minHeight) return item.minHeight;
|
||||
if (isAssigned (item.maxHeight) && preferredHeight > item.maxHeight) return item.maxHeight;
|
||||
|
||||
return preferredHeight;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
FlexBox::FlexBox() noexcept {}
|
||||
FlexBox::~FlexBox() noexcept {}
|
||||
|
||||
FlexBox::FlexBox (JustifyContent jc) noexcept : justifyContent (jc) {}
|
||||
|
||||
FlexBox::FlexBox (Direction d, Wrap w, AlignContent ac, AlignItems ai, JustifyContent jc) noexcept
|
||||
: flexDirection (d), flexWrap (w), alignContent (ac), alignItems (ai), justifyContent (jc)
|
||||
{
|
||||
}
|
||||
|
||||
void FlexBox::performLayout (Rectangle<float> targetArea)
|
||||
{
|
||||
if (! items.isEmpty())
|
||||
{
|
||||
FlexBoxLayoutCalculation layout (*this, targetArea.getWidth(), targetArea.getHeight());
|
||||
|
||||
layout.createStates();
|
||||
layout.initialiseItems();
|
||||
layout.resolveFlexibleLengths();
|
||||
layout.resolveAutoMarginsOnMainAxis();
|
||||
layout.calculateCrossSizesByLine();
|
||||
layout.calculateCrossSizeOfAllItems();
|
||||
layout.alignLinesPerAlignContent();
|
||||
layout.resolveAutoMarginsOnCrossAxis();
|
||||
layout.alignItemsInCrossAxisInLinesPerAlignItems();
|
||||
layout.alignLinesPerAlignSelf();
|
||||
layout.alignItemsByJustifyContent();
|
||||
layout.layoutAllItems();
|
||||
|
||||
for (auto& item : items)
|
||||
{
|
||||
item.currentBounds += targetArea.getPosition();
|
||||
|
||||
if (auto* comp = item.associatedComponent)
|
||||
comp->setBounds (Rectangle<int>::leftTopRightBottom ((int) item.currentBounds.getX(),
|
||||
(int) item.currentBounds.getY(),
|
||||
(int) item.currentBounds.getRight(),
|
||||
(int) item.currentBounds.getBottom()));
|
||||
|
||||
if (auto* box = item.associatedFlexBox)
|
||||
box->performLayout (item.currentBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlexBox::performLayout (Rectangle<int> targetArea)
|
||||
{
|
||||
performLayout (targetArea.toFloat());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
FlexItem::FlexItem() noexcept {}
|
||||
FlexItem::FlexItem (float w, float h) noexcept : currentBounds (w, h), minWidth (w), minHeight (h) {}
|
||||
FlexItem::FlexItem (float w, float h, Component& c) noexcept : FlexItem (w, h) { associatedComponent = &c; }
|
||||
FlexItem::FlexItem (float w, float h, FlexBox& fb) noexcept : FlexItem (w, h) { associatedFlexBox = &fb; }
|
||||
FlexItem::FlexItem (Component& c) noexcept : associatedComponent (&c) {}
|
||||
FlexItem::FlexItem (FlexBox& fb) noexcept : associatedFlexBox (&fb) {}
|
||||
|
||||
FlexItem::Margin::Margin() noexcept : left(), right(), top(), bottom() {}
|
||||
FlexItem::Margin::Margin (float v) noexcept : left (v), right (v), top (v), bottom (v) {}
|
||||
FlexItem::Margin::Margin (float t, float r, float b, float l) noexcept : left (l), right (r), top (t), bottom (b) {}
|
||||
|
||||
//==============================================================================
|
||||
FlexItem FlexItem::withFlex (float newFlexGrow) const noexcept
|
||||
{
|
||||
auto fi = *this;
|
||||
fi.flexGrow = newFlexGrow;
|
||||
return fi;
|
||||
}
|
||||
|
||||
FlexItem FlexItem::withFlex (float newFlexGrow, float newFlexShrink) const noexcept
|
||||
{
|
||||
auto fi = withFlex (newFlexGrow);
|
||||
fi.flexShrink = newFlexShrink;
|
||||
return fi;
|
||||
}
|
||||
|
||||
FlexItem FlexItem::withFlex (float newFlexGrow, float newFlexShrink, float newFlexBasis) const noexcept
|
||||
{
|
||||
auto fi = withFlex (newFlexGrow, newFlexShrink);
|
||||
fi.flexBasis = newFlexBasis;
|
||||
return fi;
|
||||
}
|
||||
|
||||
FlexItem FlexItem::withWidth (float newWidth) const noexcept { auto fi = *this; fi.width = newWidth; return fi; }
|
||||
FlexItem FlexItem::withMinWidth (float newMinWidth) const noexcept { auto fi = *this; fi.minWidth = newMinWidth; return fi; }
|
||||
FlexItem FlexItem::withMaxWidth (float newMaxWidth) const noexcept { auto fi = *this; fi.maxWidth = newMaxWidth; return fi; }
|
||||
|
||||
FlexItem FlexItem::withMinHeight (float newMinHeight) const noexcept { auto fi = *this; fi.minHeight = newMinHeight; return fi; }
|
||||
FlexItem FlexItem::withMaxHeight (float newMaxHeight) const noexcept { auto fi = *this; fi.maxHeight = newMaxHeight; return fi; }
|
||||
FlexItem FlexItem::withHeight (float newHeight) const noexcept { auto fi = *this; fi.height = newHeight; return fi; }
|
||||
|
||||
FlexItem FlexItem::withMargin (Margin m) const noexcept { auto fi = *this; fi.margin = m; return fi; }
|
||||
FlexItem FlexItem::withOrder (int newOrder) const noexcept { auto fi = *this; fi.order = newOrder; return fi; }
|
||||
FlexItem FlexItem::withAlignSelf (AlignSelf a) const noexcept { auto fi = *this; fi.alignSelf = a; return fi; }
|
||||
|
||||
} // namespace juce
|
146
deps/juce/modules/juce_gui_basics/layout/juce_FlexBox.h
vendored
Normal file
146
deps/juce/modules/juce_gui_basics/layout/juce_FlexBox.h
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/**
|
||||
Represents a FlexBox container, which contains and manages the layout of a set
|
||||
of FlexItem objects.
|
||||
|
||||
To use this class, set its parameters appropriately (you can search online for
|
||||
more help on exactly how the FlexBox protocol works!), then add your sub-items
|
||||
to the items array, and call performLayout() in the resized() function of your
|
||||
Component.
|
||||
|
||||
@see FlexItem
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FlexBox final
|
||||
{
|
||||
public:
|
||||
/** Possible values for the flexDirection property. */
|
||||
enum class Direction
|
||||
{
|
||||
row, /**< Set the main axis direction from left to right. */
|
||||
rowReverse, /**< Set the main axis direction from right to left. */
|
||||
column, /**< Set the main axis direction from top to bottom. */
|
||||
columnReverse /**< Set the main axis direction from bottom to top. */
|
||||
};
|
||||
|
||||
/** Possible values for the flexWrap property. */
|
||||
enum class Wrap
|
||||
{
|
||||
noWrap, /**< Items are forced into a single line. */
|
||||
wrap, /**< Items are wrapped onto multiple lines from top to bottom. */
|
||||
wrapReverse /**< Items are wrapped onto multiple lines from bottom to top. */
|
||||
};
|
||||
|
||||
/** Possible values for the alignContent property. */
|
||||
enum class AlignContent
|
||||
{
|
||||
stretch, /**< Lines of items are stretched from start to end of the cross axis. */
|
||||
flexStart, /**< Lines of items are aligned towards the start of the cross axis. */
|
||||
flexEnd, /**< Lines of items are aligned towards the end of the cross axis. */
|
||||
center, /**< Lines of items are aligned towards the center of the cross axis. */
|
||||
spaceBetween, /**< Lines of items are evenly spaced along the cross axis with spaces between them. */
|
||||
spaceAround /**< Lines of items are evenly spaced along the cross axis with spaces around them. */
|
||||
};
|
||||
|
||||
/** Possible values for the alignItems property. */
|
||||
enum class AlignItems
|
||||
{
|
||||
stretch, /**< Items are stretched from start to end of the cross axis. */
|
||||
flexStart, /**< Items are aligned towards the start of the cross axis. */
|
||||
flexEnd, /**< Items are aligned towards the end of the cross axis. */
|
||||
center /**< Items are aligned towards the center of the cross axis. */
|
||||
};
|
||||
|
||||
/** Possible values for the justifyContent property. */
|
||||
enum class JustifyContent
|
||||
{
|
||||
flexStart, /**< Items are justified towards the start of the main axis. */
|
||||
flexEnd, /**< Items are justified towards the end of the main axis. */
|
||||
center, /**< Items are justified towards the center of the main axis. */
|
||||
spaceBetween, /**< Items are evenly spaced along the main axis with spaces between them. */
|
||||
spaceAround /**< Items are evenly spaced along the main axis with spaces around them. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an empty FlexBox container with default parameters. */
|
||||
FlexBox() noexcept;
|
||||
|
||||
/** Creates an empty FlexBox container with these parameters. */
|
||||
FlexBox (Direction, Wrap, AlignContent, AlignItems, JustifyContent) noexcept;
|
||||
|
||||
/** Creates an empty FlexBox container with the given content-justification mode. */
|
||||
FlexBox (JustifyContent) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~FlexBox() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Lays-out the box's items within the given rectangle. */
|
||||
void performLayout (Rectangle<float> targetArea);
|
||||
|
||||
/** Lays-out the box's items within the given rectangle. */
|
||||
void performLayout (Rectangle<int> targetArea);
|
||||
|
||||
//==============================================================================
|
||||
/** Specifies how flex items are placed in the flex container, and defines the
|
||||
direction of the main axis.
|
||||
*/
|
||||
Direction flexDirection = Direction::row;
|
||||
|
||||
/** Specifies whether items are forced into a single line or can be wrapped onto multiple lines.
|
||||
If wrapping is allowed, this property also controls the direction in which lines are stacked.
|
||||
*/
|
||||
Wrap flexWrap = Wrap::noWrap;
|
||||
|
||||
/** Specifies how a flex container's lines are placed within the flex container when
|
||||
there is extra space on the cross-axis.
|
||||
This property has no effect on single line layouts.
|
||||
*/
|
||||
AlignContent alignContent = AlignContent::stretch;
|
||||
|
||||
/** Specifies the alignment of flex items along the cross-axis of each line. */
|
||||
AlignItems alignItems = AlignItems::stretch;
|
||||
|
||||
/** Defines how the container distributes space between and around items along the main-axis.
|
||||
The alignment is done after the lengths and auto margins are applied, so that if there is at
|
||||
least one flexible element, with flex-grow different from 0, it will have no effect as there
|
||||
won't be any available space.
|
||||
*/
|
||||
JustifyContent justifyContent = JustifyContent::flexStart;
|
||||
|
||||
/** The set of items to lay-out. */
|
||||
Array<FlexItem> items;
|
||||
|
||||
private:
|
||||
JUCE_LEAK_DETECTOR (FlexBox)
|
||||
};
|
||||
|
||||
} // namespace juce
|
177
deps/juce/modules/juce_gui_basics/layout/juce_FlexItem.h
vendored
Normal file
177
deps/juce/modules/juce_gui_basics/layout/juce_FlexItem.h
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/**
|
||||
Describes the properties of an item inside a FlexBox container.
|
||||
|
||||
@see FlexBox
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FlexItem final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an item with default parameters, and zero size. */
|
||||
FlexItem() noexcept;
|
||||
|
||||
/** Creates an item with the given size. */
|
||||
FlexItem (float width, float height) noexcept;
|
||||
|
||||
/** Creates an item with the given size and target Component. */
|
||||
FlexItem (float width, float height, Component& targetComponent) noexcept;
|
||||
|
||||
/** Creates an item that represents an embedded FlexBox with a given size. */
|
||||
FlexItem (float width, float height, FlexBox& flexBoxToControl) noexcept;
|
||||
|
||||
/** Creates an item with a given target Component. */
|
||||
FlexItem (Component& componentToControl) noexcept;
|
||||
|
||||
/** Creates an item that represents an embedded FlexBox. This class will not
|
||||
create a copy of the supplied flex box. You need to ensure that the
|
||||
life-time of flexBoxToControl is longer than the FlexItem. */
|
||||
FlexItem (FlexBox& flexBoxToControl) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The item's current bounds. */
|
||||
Rectangle<float> currentBounds;
|
||||
|
||||
/** If this is non-null, it represents a Component whose bounds are controlled by this item. */
|
||||
Component* associatedComponent = nullptr;
|
||||
|
||||
/** If this is non-null, it represents a FlexBox whose bounds are controlled by this item. */
|
||||
FlexBox* associatedFlexBox = nullptr;
|
||||
|
||||
/** Determines the order used to lay out items in their flex container.
|
||||
Elements are laid out in ascending order of thus order value. Elements with the same order value
|
||||
are laid out in the order in which they appear in the array.
|
||||
*/
|
||||
int order = 0;
|
||||
|
||||
/** Specifies the flex grow factor of this item.
|
||||
This indicates the amount of space inside the flex container the item should take up.
|
||||
*/
|
||||
float flexGrow = 0.0f;
|
||||
|
||||
/** Specifies the flex shrink factor of the item.
|
||||
This indicates the rate at which the item shrinks if there is insufficient space in
|
||||
the container.
|
||||
*/
|
||||
float flexShrink = 1.0f;
|
||||
|
||||
/** Specifies the flex-basis of the item.
|
||||
This is the initial main size of a flex item in the direction of flow. It determines the size
|
||||
of the content-box unless specified otherwise using box-sizing.
|
||||
*/
|
||||
float flexBasis = 0.0f;
|
||||
|
||||
/** Possible value for the alignSelf property */
|
||||
enum class AlignSelf
|
||||
{
|
||||
autoAlign, /**< Follows the FlexBox container's alignItems property. */
|
||||
flexStart, /**< Item is aligned towards the start of the cross axis. */
|
||||
flexEnd, /**< Item is aligned towards the end of the cross axis. */
|
||||
center, /**< Item is aligned towards the center of the cross axis. */
|
||||
stretch /**< Item is stretched from start to end of the cross axis. */
|
||||
};
|
||||
|
||||
/** This is the align-self property of the item.
|
||||
This determines the alignment of the item along the cross-axis (perpendicular to the direction
|
||||
of flow).
|
||||
*/
|
||||
AlignSelf alignSelf = AlignSelf::stretch;
|
||||
|
||||
//==============================================================================
|
||||
/** This constant can be used for sizes to indicate that 'auto' mode should be used. */
|
||||
static const int autoValue = -2;
|
||||
/** This constant can be used for sizes to indicate that no value has been set. */
|
||||
static const int notAssigned = -1;
|
||||
|
||||
float width = (float) notAssigned; /**< The item's width. */
|
||||
float minWidth = 0.0f; /**< The item's minimum width */
|
||||
float maxWidth = (float) notAssigned; /**< The item's maximum width */
|
||||
|
||||
float height = (float) notAssigned; /**< The item's height */
|
||||
float minHeight = 0.0f; /**< The item's minimum height */
|
||||
float maxHeight = (float) notAssigned; /**< The item's maximum height */
|
||||
|
||||
/** Represents a margin. */
|
||||
struct Margin final
|
||||
{
|
||||
Margin() noexcept; /**< Creates a margin of size zero. */
|
||||
Margin (float size) noexcept; /**< Creates a margin with this size on all sides. */
|
||||
Margin (float top, float right, float bottom, float left) noexcept; /**< Creates a margin with these sizes. */
|
||||
|
||||
float left; /**< Left margin size */
|
||||
float right; /**< Right margin size */
|
||||
float top; /**< Top margin size */
|
||||
float bottom; /**< Bottom margin size */
|
||||
};
|
||||
|
||||
/** The margin to leave around this item. */
|
||||
Margin margin;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a copy of this object with a new flex-grow value. */
|
||||
FlexItem withFlex (float newFlexGrow) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with new flex-grow and flex-shrink values. */
|
||||
FlexItem withFlex (float newFlexGrow, float newFlexShrink) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with new flex-grow, flex-shrink and flex-basis values. */
|
||||
FlexItem withFlex (float newFlexGrow, float newFlexShrink, float newFlexBasis) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new width. */
|
||||
FlexItem withWidth (float newWidth) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new minimum width. */
|
||||
FlexItem withMinWidth (float newMinWidth) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new maximum width. */
|
||||
FlexItem withMaxWidth (float newMaxWidth) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new height. */
|
||||
FlexItem withHeight (float newHeight) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new minimum height. */
|
||||
FlexItem withMinHeight (float newMinHeight) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new maximum height. */
|
||||
FlexItem withMaxHeight (float newMaxHeight) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new margin. */
|
||||
FlexItem withMargin (Margin) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new order. */
|
||||
FlexItem withOrder (int newOrder) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new alignSelf value. */
|
||||
FlexItem withAlignSelf (AlignSelf newAlignSelf) const noexcept;
|
||||
};
|
||||
|
||||
} // namespace juce
|
1344
deps/juce/modules/juce_gui_basics/layout/juce_Grid.cpp
vendored
Normal file
1344
deps/juce/modules/juce_gui_basics/layout/juce_Grid.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
229
deps/juce/modules/juce_gui_basics/layout/juce_Grid.h
vendored
Normal file
229
deps/juce/modules/juce_gui_basics/layout/juce_Grid.h
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/**
|
||||
Container that handles geometry for grid layouts (fixed columns and rows) using a set of declarative rules.
|
||||
|
||||
Implemented from the `CSS Grid Layout` specification as described at:
|
||||
https://css-tricks.com/snippets/css/complete-guide-grid/
|
||||
|
||||
@see GridItem
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API Grid final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** A size in pixels */
|
||||
struct Px final
|
||||
{
|
||||
explicit Px (float p) : pixels (static_cast<long double>(p)) { /*sta (p >= 0.0f);*/ }
|
||||
explicit Px (int p) : pixels (static_cast<long double>(p)) { /*sta (p >= 0.0f);*/ }
|
||||
explicit constexpr Px (long double p) : pixels (p) {}
|
||||
explicit constexpr Px (unsigned long long p) : pixels (static_cast<long double>(p)) {}
|
||||
|
||||
long double pixels;
|
||||
};
|
||||
|
||||
/** A fractional ratio integer */
|
||||
struct Fr final
|
||||
{
|
||||
explicit Fr (int f) : fraction (static_cast<unsigned long long> (f)) {}
|
||||
explicit constexpr Fr (unsigned long long p) : fraction (p) {}
|
||||
|
||||
unsigned long long fraction;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Represents a track. */
|
||||
struct TrackInfo final
|
||||
{
|
||||
/** Creates a track with auto dimension. */
|
||||
TrackInfo() noexcept;
|
||||
|
||||
TrackInfo (Px sizeInPixels) noexcept;
|
||||
TrackInfo (Fr fractionOfFreeSpace) noexcept;
|
||||
|
||||
TrackInfo (Px sizeInPixels, const String& endLineNameToUse) noexcept;
|
||||
TrackInfo (Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept;
|
||||
|
||||
TrackInfo (const String& startLineNameToUse, Px sizeInPixels) noexcept;
|
||||
TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace) noexcept;
|
||||
|
||||
TrackInfo (const String& startLineNameToUse, Px sizeInPixels, const String& endLineNameToUse) noexcept;
|
||||
TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept;
|
||||
|
||||
bool isAuto() const noexcept { return hasKeyword; }
|
||||
bool isFractional() const noexcept { return isFraction; }
|
||||
bool isPixels() const noexcept { return ! isFraction; }
|
||||
const String& getStartLineName() const noexcept { return startLineName; }
|
||||
const String& getEndLineName() const noexcept { return endLineName; }
|
||||
|
||||
/** Get the track's size - which might mean an absolute pixels value or a fractional ratio. */
|
||||
float getSize() const noexcept { return size; }
|
||||
|
||||
private:
|
||||
friend class Grid;
|
||||
float getAbsoluteSize (float relativeFractionalUnit) const;
|
||||
|
||||
float size = 0; // Either a fraction or an absolute size in pixels
|
||||
bool isFraction = false;
|
||||
bool hasKeyword = false;
|
||||
|
||||
String startLineName, endLineName;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Possible values for the justifyItems property. */
|
||||
enum class JustifyItems : int
|
||||
{
|
||||
start = 0, /**< Content inside the item is justified towards the left. */
|
||||
end, /**< Content inside the item is justified towards the right. */
|
||||
center, /**< Content inside the item is justified towards the center. */
|
||||
stretch /**< Content inside the item is stretched from left to right. */
|
||||
};
|
||||
|
||||
/** Possible values for the alignItems property. */
|
||||
enum class AlignItems : int
|
||||
{
|
||||
start = 0, /**< Content inside the item is aligned towards the top. */
|
||||
end, /**< Content inside the item is aligned towards the bottom. */
|
||||
center, /**< Content inside the item is aligned towards the center. */
|
||||
stretch /**< Content inside the item is stretched from top to bottom. */
|
||||
};
|
||||
|
||||
/** Possible values for the justifyContent property. */
|
||||
enum class JustifyContent
|
||||
{
|
||||
start, /**< Items are justified towards the left of the container. */
|
||||
end, /**< Items are justified towards the right of the container. */
|
||||
center, /**< Items are justified towards the center of the container. */
|
||||
stretch, /**< Items are stretched from left to right of the container. */
|
||||
spaceAround, /**< Items are evenly spaced along the row with spaces between them. */
|
||||
spaceBetween, /**< Items are evenly spaced along the row with spaces around them. */
|
||||
spaceEvenly /**< Items are evenly spaced along the row with even amount of spaces between them. */
|
||||
};
|
||||
|
||||
/** Possible values for the alignContent property. */
|
||||
enum class AlignContent
|
||||
{
|
||||
start, /**< Items are aligned towards the top of the container. */
|
||||
end, /**< Items are aligned towards the bottom of the container. */
|
||||
center, /**< Items are aligned towards the center of the container. */
|
||||
stretch, /**< Items are stretched from top to bottom of the container. */
|
||||
spaceAround, /**< Items are evenly spaced along the column with spaces between them. */
|
||||
spaceBetween, /**< Items are evenly spaced along the column with spaces around them. */
|
||||
spaceEvenly /**< Items are evenly spaced along the column with even amount of spaces between them. */
|
||||
};
|
||||
|
||||
/** Possible values for the autoFlow property. */
|
||||
enum class AutoFlow
|
||||
{
|
||||
row, /**< Fills the grid by adding rows of items. */
|
||||
column, /**< Fills the grid by adding columns of items. */
|
||||
rowDense, /**< Fills the grid by adding rows of items and attempts to fill in gaps. */
|
||||
columnDense /**< Fills the grid by adding columns of items and attempts to fill in gaps. */
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an empty Grid container with default parameters. */
|
||||
Grid() = default;
|
||||
|
||||
/** Destructor */
|
||||
~Grid() noexcept = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Specifies the alignment of content inside the items along the rows. */
|
||||
JustifyItems justifyItems = JustifyItems::stretch;
|
||||
|
||||
/** Specifies the alignment of content inside the items along the columns. */
|
||||
AlignItems alignItems = AlignItems::stretch;
|
||||
|
||||
/** Specifies the alignment of items along the rows. */
|
||||
JustifyContent justifyContent = JustifyContent::stretch;
|
||||
|
||||
/** Specifies the alignment of items along the columns. */
|
||||
AlignContent alignContent = AlignContent::stretch;
|
||||
|
||||
/** Specifies how the auto-placement algorithm places items. */
|
||||
AutoFlow autoFlow = AutoFlow::row;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** The set of column tracks to lay out. */
|
||||
Array<TrackInfo> templateColumns;
|
||||
|
||||
/** The set of row tracks to lay out. */
|
||||
Array<TrackInfo> templateRows;
|
||||
|
||||
/** Template areas */
|
||||
StringArray templateAreas;
|
||||
|
||||
/** The row track for auto dimension. */
|
||||
TrackInfo autoRows;
|
||||
|
||||
/** The column track for auto dimension. */
|
||||
TrackInfo autoColumns;
|
||||
|
||||
/** The gap in pixels between columns. */
|
||||
Px columnGap { 0 };
|
||||
/** The gap in pixels between rows. */
|
||||
Px rowGap { 0 };
|
||||
|
||||
/** Sets the gap between rows and columns in pixels. */
|
||||
void setGap (Px sizeInPixels) noexcept { rowGap = columnGap = sizeInPixels; }
|
||||
|
||||
//==============================================================================
|
||||
/** The set of items to lay-out. */
|
||||
Array<GridItem> items;
|
||||
|
||||
//==============================================================================
|
||||
/** Lays-out the grid's items within the given rectangle. */
|
||||
void performLayout (Rectangle<int>);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of columns. */
|
||||
int getNumberOfColumns() const noexcept { return templateColumns.size(); }
|
||||
/** Returns the number of rows. */
|
||||
int getNumberOfRows() const noexcept { return templateRows.size(); }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct SizeCalculation;
|
||||
struct PlacementHelpers;
|
||||
struct AutoPlacement;
|
||||
struct BoxAlignment;
|
||||
};
|
||||
|
||||
constexpr Grid::Px operator"" _px (long double px) { return Grid::Px { px }; }
|
||||
constexpr Grid::Px operator"" _px (unsigned long long px) { return Grid::Px { px }; }
|
||||
constexpr Grid::Fr operator"" _fr (unsigned long long fr) { return Grid::Fr { fr }; }
|
||||
|
||||
} // namespace juce
|
181
deps/juce/modules/juce_gui_basics/layout/juce_GridItem.cpp
vendored
Normal file
181
deps/juce/modules/juce_gui_basics/layout/juce_GridItem.cpp
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
GridItem::Property::Property() noexcept : isAuto (true)
|
||||
{
|
||||
}
|
||||
|
||||
GridItem::Property::Property (GridItem::Keyword keyword) noexcept : isAuto (keyword == GridItem::Keyword::autoValue)
|
||||
{
|
||||
jassert (keyword == GridItem::Keyword::autoValue);
|
||||
}
|
||||
|
||||
GridItem::Property::Property (const char* lineNameToUse) noexcept : GridItem::Property (String (lineNameToUse))
|
||||
{
|
||||
}
|
||||
|
||||
GridItem::Property::Property (const String& lineNameToUse) noexcept : name (lineNameToUse), number (1)
|
||||
{
|
||||
}
|
||||
|
||||
GridItem::Property::Property (int numberToUse) noexcept : number (numberToUse)
|
||||
{
|
||||
}
|
||||
|
||||
GridItem::Property::Property (int numberToUse, const String& lineNameToUse) noexcept
|
||||
: name (lineNameToUse), number (numberToUse)
|
||||
{
|
||||
}
|
||||
|
||||
GridItem::Property::Property (Span spanToUse) noexcept
|
||||
: name (spanToUse.name), number (spanToUse.number), isSpan (true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
GridItem::Margin::Margin() noexcept : left(), right(), top(), bottom() {}
|
||||
|
||||
GridItem::Margin::Margin (int v) noexcept : GridItem::Margin::Margin (static_cast<float> (v)) {}
|
||||
|
||||
GridItem::Margin::Margin (float v) noexcept : left (v), right (v), top (v), bottom (v) {}
|
||||
|
||||
GridItem::Margin::Margin (float t, float r, float b, float l) noexcept : left (l), right (r), top (t), bottom (b) {}
|
||||
|
||||
//==============================================================================
|
||||
GridItem::GridItem() noexcept {}
|
||||
GridItem::~GridItem() noexcept {}
|
||||
|
||||
GridItem::GridItem (Component& componentToUse) noexcept : associatedComponent (&componentToUse) {}
|
||||
GridItem::GridItem (Component* componentToUse) noexcept : associatedComponent (componentToUse) {}
|
||||
|
||||
void GridItem::setArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd)
|
||||
{
|
||||
column.start = columnStart;
|
||||
column.end = columnEnd;
|
||||
row.start = rowStart;
|
||||
row.end = rowEnd;
|
||||
}
|
||||
|
||||
void GridItem::setArea (Property rowStart, Property columnStart)
|
||||
{
|
||||
column.start = columnStart;
|
||||
row.start = rowStart;
|
||||
}
|
||||
|
||||
void GridItem::setArea (const String& areaName)
|
||||
{
|
||||
area = areaName;
|
||||
}
|
||||
|
||||
GridItem GridItem::withArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.setArea (rowStart, columnStart, rowEnd, columnEnd);
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withArea (Property rowStart, Property columnStart) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.setArea (rowStart, columnStart);
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withArea (const String& areaName) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.setArea (areaName);
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withRow (StartAndEndProperty newRow) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.row = newRow;
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withColumn (StartAndEndProperty newColumn) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.column = newColumn;
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withAlignSelf (AlignSelf newAlignSelf) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.alignSelf = newAlignSelf;
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withJustifySelf (JustifySelf newJustifySelf) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.justifySelf = newJustifySelf;
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withWidth (float newWidth) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.width = newWidth;
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withHeight (float newHeight) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.height = newHeight;
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withSize (float newWidth, float newHeight) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.width = newWidth;
|
||||
gi.height = newHeight;
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withMargin (Margin newHeight) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.margin = newHeight;
|
||||
return gi;
|
||||
}
|
||||
|
||||
GridItem GridItem::withOrder (int newOrder) const noexcept
|
||||
{
|
||||
auto gi = *this;
|
||||
gi.order = newOrder;
|
||||
return gi;
|
||||
}
|
||||
|
||||
} // namespace juce
|
243
deps/juce/modules/juce_gui_basics/layout/juce_GridItem.h
vendored
Normal file
243
deps/juce/modules/juce_gui_basics/layout/juce_GridItem.h
vendored
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/**
|
||||
Defines an item in a Grid
|
||||
@see Grid
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API GridItem
|
||||
{
|
||||
public:
|
||||
enum class Keyword { autoValue };
|
||||
|
||||
//==============================================================================
|
||||
/** Represents a span. */
|
||||
struct Span
|
||||
{
|
||||
explicit Span (int numberToUse) noexcept : number (numberToUse)
|
||||
{
|
||||
/* Span must be at least one and positive */
|
||||
jassert (numberToUse > 0);
|
||||
}
|
||||
|
||||
explicit Span (int numberToUse, const String& nameToUse) : Span (numberToUse)
|
||||
{
|
||||
/* Name must not be empty */
|
||||
jassert (nameToUse.isNotEmpty());
|
||||
name = nameToUse;
|
||||
}
|
||||
|
||||
explicit Span (const String& nameToUse) : name (nameToUse)
|
||||
{
|
||||
/* Name must not be empty */
|
||||
jassert (nameToUse.isNotEmpty());
|
||||
}
|
||||
|
||||
int number = 1;
|
||||
String name;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Represents a property. */
|
||||
struct Property
|
||||
{
|
||||
Property() noexcept;
|
||||
|
||||
Property (Keyword keyword) noexcept;
|
||||
|
||||
Property (const char* lineNameToUse) noexcept;
|
||||
|
||||
Property (const String& lineNameToUse) noexcept;
|
||||
|
||||
Property (int numberToUse) noexcept;
|
||||
|
||||
Property (int numberToUse, const String& lineNameToUse) noexcept;
|
||||
|
||||
Property (Span spanToUse) noexcept;
|
||||
|
||||
bool hasSpan() const noexcept { return isSpan && ! isAuto; }
|
||||
bool hasAbsolute() const noexcept { return ! (isSpan || isAuto); }
|
||||
bool hasAuto() const noexcept { return isAuto; }
|
||||
bool hasName() const noexcept { return name.isNotEmpty(); }
|
||||
const String& getName() const noexcept { return name; }
|
||||
int getNumber() const noexcept { return number; }
|
||||
|
||||
private:
|
||||
String name;
|
||||
int number = 1; /** Either an absolute line number or number of lines to span across. */
|
||||
bool isSpan = false;
|
||||
bool isAuto = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Represents start and end properties. */
|
||||
struct StartAndEndProperty { Property start, end; };
|
||||
|
||||
//==============================================================================
|
||||
/** Possible values for the justifySelf property. */
|
||||
enum class JustifySelf : int
|
||||
{
|
||||
start = 0, /**< Content inside the item is justified towards the left. */
|
||||
end, /**< Content inside the item is justified towards the right. */
|
||||
center, /**< Content inside the item is justified towards the center. */
|
||||
stretch, /**< Content inside the item is stretched from left to right. */
|
||||
autoValue /**< Follows the Grid container's justifyItems property. */
|
||||
};
|
||||
|
||||
/** Possible values for the alignSelf property. */
|
||||
enum class AlignSelf : int
|
||||
{
|
||||
start = 0, /**< Content inside the item is aligned towards the top. */
|
||||
end, /**< Content inside the item is aligned towards the bottom. */
|
||||
center, /**< Content inside the item is aligned towards the center. */
|
||||
stretch, /**< Content inside the item is stretched from top to bottom. */
|
||||
autoValue /**< Follows the Grid container's alignItems property. */
|
||||
};
|
||||
|
||||
/** Creates an item with default parameters. */
|
||||
GridItem() noexcept;
|
||||
/** Creates an item with a given Component to use. */
|
||||
GridItem (Component& componentToUse) noexcept;
|
||||
/** Creates an item with a given Component to use. */
|
||||
GridItem (Component* componentToUse) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~GridItem() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** If this is non-null, it represents a Component whose bounds are controlled by this item. */
|
||||
Component* associatedComponent = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
/** Determines the order used to lay out items in their grid container. */
|
||||
int order = 0;
|
||||
|
||||
/** This is the justify-self property of the item.
|
||||
This determines the alignment of the item along the row.
|
||||
*/
|
||||
JustifySelf justifySelf = JustifySelf::autoValue;
|
||||
|
||||
/** This is the align-self property of the item.
|
||||
This determines the alignment of the item along the column.
|
||||
*/
|
||||
AlignSelf alignSelf = AlignSelf::autoValue;
|
||||
|
||||
/** These are the start and end properties of the column. */
|
||||
StartAndEndProperty column = { Keyword::autoValue, Keyword::autoValue };
|
||||
|
||||
/** These are the start and end properties of the row. */
|
||||
StartAndEndProperty row = { Keyword::autoValue, Keyword::autoValue };
|
||||
|
||||
/** */
|
||||
String area;
|
||||
|
||||
//==============================================================================
|
||||
enum
|
||||
{
|
||||
useDefaultValue = -2, /* TODO: useDefaultValue should be named useAuto */
|
||||
notAssigned = -1
|
||||
};
|
||||
|
||||
/* TODO: move all of this into a common class that is shared with the FlexItem */
|
||||
float width = notAssigned;
|
||||
float minWidth = 0.0f;
|
||||
float maxWidth = notAssigned;
|
||||
|
||||
float height = notAssigned;
|
||||
float minHeight = 0.0f;
|
||||
float maxHeight = notAssigned;
|
||||
|
||||
/** Represents a margin. */
|
||||
struct Margin
|
||||
{
|
||||
Margin() noexcept;
|
||||
Margin (int size) noexcept;
|
||||
Margin (float size) noexcept;
|
||||
Margin (float top, float right, float bottom, float left) noexcept; /**< Creates a margin with these sizes. */
|
||||
|
||||
float left;
|
||||
float right;
|
||||
float top;
|
||||
float bottom;
|
||||
};
|
||||
|
||||
/** The margin to leave around this item. */
|
||||
Margin margin;
|
||||
|
||||
/** The item's current bounds. */
|
||||
Rectangle<float> currentBounds;
|
||||
|
||||
/** Short-hand */
|
||||
void setArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd);
|
||||
|
||||
/** Short-hand, span of 1 by default */
|
||||
void setArea (Property rowStart, Property columnStart);
|
||||
|
||||
/** Short-hand */
|
||||
void setArea (const String& areaName);
|
||||
|
||||
/** Short-hand */
|
||||
GridItem withArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd) const noexcept;
|
||||
|
||||
/** Short-hand, span of 1 by default */
|
||||
GridItem withArea (Property rowStart, Property columnStart) const noexcept;
|
||||
|
||||
/** Short-hand */
|
||||
GridItem withArea (const String& areaName) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new row property. */
|
||||
GridItem withRow (StartAndEndProperty row) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new column property. */
|
||||
GridItem withColumn (StartAndEndProperty column) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new alignSelf property. */
|
||||
GridItem withAlignSelf (AlignSelf newAlignSelf) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new justifySelf property. */
|
||||
GridItem withJustifySelf (JustifySelf newJustifySelf) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new width. */
|
||||
GridItem withWidth (float newWidth) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new height. */
|
||||
GridItem withHeight (float newHeight) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new size. */
|
||||
GridItem withSize (float newWidth, float newHeight) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new margin. */
|
||||
GridItem withMargin (Margin newMargin) const noexcept;
|
||||
|
||||
/** Returns a copy of this object with a new order. */
|
||||
GridItem withOrder (int newOrder) const noexcept;
|
||||
};
|
||||
|
||||
} // namespace juce
|
78
deps/juce/modules/juce_gui_basics/layout/juce_GroupComponent.cpp
vendored
Normal file
78
deps/juce/modules/juce_gui_basics/layout/juce_GroupComponent.cpp
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
GroupComponent::GroupComponent (const String& name,
|
||||
const String& labelText)
|
||||
: Component (name),
|
||||
text (labelText),
|
||||
justification (Justification::left)
|
||||
{
|
||||
setInterceptsMouseClicks (false, true);
|
||||
}
|
||||
|
||||
GroupComponent::~GroupComponent() {}
|
||||
|
||||
void GroupComponent::setText (const String& newText)
|
||||
{
|
||||
if (text != newText)
|
||||
{
|
||||
text = newText;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
String GroupComponent::getText() const
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
void GroupComponent::setTextLabelPosition (Justification newJustification)
|
||||
{
|
||||
if (justification != newJustification)
|
||||
{
|
||||
justification = newJustification;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupComponent::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawGroupComponentOutline (g, getWidth(), getHeight(),
|
||||
text, justification, *this);
|
||||
}
|
||||
|
||||
void GroupComponent::enablementChanged() { repaint(); }
|
||||
void GroupComponent::colourChanged() { repaint(); }
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> GroupComponent::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
|
||||
}
|
||||
|
||||
} // namespace juce
|
111
deps/juce/modules/juce_gui_basics/layout/juce_GroupComponent.h
vendored
Normal file
111
deps/juce/modules/juce_gui_basics/layout/juce_GroupComponent.h
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that draws an outline around itself and has an optional title at
|
||||
the top, for drawing an outline around a group of controls.
|
||||
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API GroupComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a GroupComponent.
|
||||
|
||||
@param componentName the name to give the component
|
||||
@param labelText the text to show at the top of the outline
|
||||
*/
|
||||
GroupComponent (const String& componentName = {},
|
||||
const String& labelText = {});
|
||||
|
||||
/** Destructor. */
|
||||
~GroupComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the text that's shown at the top of the component. */
|
||||
void setText (const String& newText);
|
||||
|
||||
/** Returns the currently displayed text label. */
|
||||
String getText() const;
|
||||
|
||||
/** Sets the positioning of the text label.
|
||||
(The default is Justification::left)
|
||||
@see getTextLabelPosition
|
||||
*/
|
||||
void setTextLabelPosition (Justification justification);
|
||||
|
||||
/** Returns the current text label position.
|
||||
@see setTextLabelPosition
|
||||
*/
|
||||
Justification getTextLabelPosition() const noexcept { return justification; }
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the component.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
outlineColourId = 0x1005400, /**< The colour to use for drawing the line around the edge. */
|
||||
textColourId = 0x1005410 /**< The colour to use to draw the text label. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes. */
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual void drawGroupComponentOutline (Graphics&, int w, int h, const String& text,
|
||||
const Justification&, GroupComponent&) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void enablementChanged() override;
|
||||
/** @internal */
|
||||
void colourChanged() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
|
||||
String text;
|
||||
Justification justification;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (GroupComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
614
deps/juce/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp
vendored
Normal file
614
deps/juce/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.cpp
vendored
Normal file
@ -0,0 +1,614 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
MultiDocumentPanelWindow::MultiDocumentPanelWindow (Colour backgroundColour)
|
||||
: DocumentWindow (String(), backgroundColour,
|
||||
DocumentWindow::maximiseButton | DocumentWindow::closeButton, false)
|
||||
{
|
||||
}
|
||||
|
||||
MultiDocumentPanelWindow::~MultiDocumentPanelWindow()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MultiDocumentPanelWindow::maximiseButtonPressed()
|
||||
{
|
||||
if (auto* owner = getOwner())
|
||||
owner->setLayoutMode (MultiDocumentPanel::MaximisedWindowsWithTabs);
|
||||
else
|
||||
jassertfalse; // these windows are only designed to be used inside a MultiDocumentPanel!
|
||||
}
|
||||
|
||||
void MultiDocumentPanelWindow::closeButtonPressed()
|
||||
{
|
||||
if (auto* owner = getOwner())
|
||||
owner->closeDocumentAsync (getContentComponent(), true, nullptr);
|
||||
else
|
||||
jassertfalse; // these windows are only designed to be used inside a MultiDocumentPanel!
|
||||
}
|
||||
|
||||
void MultiDocumentPanelWindow::activeWindowStatusChanged()
|
||||
{
|
||||
DocumentWindow::activeWindowStatusChanged();
|
||||
updateOrder();
|
||||
}
|
||||
|
||||
void MultiDocumentPanelWindow::broughtToFront()
|
||||
{
|
||||
DocumentWindow::broughtToFront();
|
||||
updateOrder();
|
||||
}
|
||||
|
||||
void MultiDocumentPanelWindow::updateOrder()
|
||||
{
|
||||
if (auto* owner = getOwner())
|
||||
owner->updateOrder();
|
||||
}
|
||||
|
||||
MultiDocumentPanel* MultiDocumentPanelWindow::getOwner() const noexcept
|
||||
{
|
||||
return findParentComponentOfClass<MultiDocumentPanel>();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct MultiDocumentPanel::TabbedComponentInternal : public TabbedComponent
|
||||
{
|
||||
TabbedComponentInternal() : TabbedComponent (TabbedButtonBar::TabsAtTop) {}
|
||||
|
||||
void currentTabChanged (int, const String&) override
|
||||
{
|
||||
if (auto* owner = findParentComponentOfClass<MultiDocumentPanel>())
|
||||
owner->updateOrder();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
MultiDocumentPanel::MultiDocumentPanel()
|
||||
{
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
MultiDocumentPanel::~MultiDocumentPanel() = default;
|
||||
|
||||
//==============================================================================
|
||||
namespace MultiDocHelpers
|
||||
{
|
||||
static bool shouldDeleteComp (Component* const c)
|
||||
{
|
||||
return c->getProperties() ["mdiDocumentDelete_"];
|
||||
}
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool MultiDocumentPanel::closeAllDocuments (const bool checkItsOkToCloseFirst)
|
||||
{
|
||||
while (! components.isEmpty())
|
||||
if (! closeDocument (components.getLast(), checkItsOkToCloseFirst))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void MultiDocumentPanel::closeLastDocumentRecursive (SafePointer<MultiDocumentPanel> parent,
|
||||
bool checkItsOkToCloseFirst,
|
||||
std::function<void (bool)> callback)
|
||||
{
|
||||
if (parent->components.isEmpty())
|
||||
{
|
||||
if (callback != nullptr)
|
||||
callback (true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
parent->closeDocumentAsync (parent->components.getLast(),
|
||||
checkItsOkToCloseFirst,
|
||||
[parent, checkItsOkToCloseFirst, callback] (bool closeResult)
|
||||
{
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
if (! closeResult)
|
||||
{
|
||||
if (callback != nullptr)
|
||||
callback (false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
parent->closeLastDocumentRecursive (parent, checkItsOkToCloseFirst, std::move (callback));
|
||||
});
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::closeAllDocumentsAsync (bool checkItsOkToCloseFirst, std::function<void (bool)> callback)
|
||||
{
|
||||
closeLastDocumentRecursive (this, checkItsOkToCloseFirst, std::move (callback));
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool MultiDocumentPanel::tryToCloseDocument (Component*)
|
||||
{
|
||||
// If you hit this assertion then you need to implement this method in a subclass.
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
MultiDocumentPanelWindow* MultiDocumentPanel::createNewDocumentWindow()
|
||||
{
|
||||
return new MultiDocumentPanelWindow (backgroundColour);
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::addWindow (Component* component)
|
||||
{
|
||||
auto* dw = createNewDocumentWindow();
|
||||
|
||||
dw->setResizable (true, false);
|
||||
dw->setContentNonOwned (component, true);
|
||||
dw->setName (component->getName());
|
||||
|
||||
auto bkg = component->getProperties() ["mdiDocumentBkg_"];
|
||||
dw->setBackgroundColour (bkg.isVoid() ? backgroundColour : Colour ((uint32) static_cast<int> (bkg)));
|
||||
|
||||
int x = 4;
|
||||
|
||||
if (auto* topComp = getChildren().getLast())
|
||||
if (topComp->getX() == x && topComp->getY() == x)
|
||||
x += 16;
|
||||
|
||||
dw->setTopLeftPosition (x, x);
|
||||
|
||||
auto pos = component->getProperties() ["mdiDocumentPos_"];
|
||||
if (pos.toString().isNotEmpty())
|
||||
dw->restoreWindowStateFromString (pos.toString());
|
||||
|
||||
addAndMakeVisible (dw);
|
||||
dw->toFront (true);
|
||||
}
|
||||
|
||||
bool MultiDocumentPanel::addDocument (Component* const component,
|
||||
Colour docColour,
|
||||
const bool deleteWhenRemoved)
|
||||
{
|
||||
// If you try passing a full DocumentWindow or ResizableWindow in here, you'll end up
|
||||
// with a frame-within-a-frame! Just pass in the bare content component.
|
||||
jassert (dynamic_cast<ResizableWindow*> (component) == nullptr);
|
||||
|
||||
if (component == nullptr || (maximumNumDocuments > 0 && components.size() >= maximumNumDocuments))
|
||||
return false;
|
||||
|
||||
components.add (component);
|
||||
component->getProperties().set ("mdiDocumentDelete_", deleteWhenRemoved);
|
||||
component->getProperties().set ("mdiDocumentBkg_", (int) docColour.getARGB());
|
||||
component->addComponentListener (this);
|
||||
|
||||
if (mode == FloatingWindows)
|
||||
{
|
||||
if (isFullscreenWhenOneDocument())
|
||||
{
|
||||
if (components.size() == 1)
|
||||
{
|
||||
addAndMakeVisible (component);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (components.size() == 2)
|
||||
addWindow (components.getFirst());
|
||||
|
||||
addWindow (component);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addWindow (component);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tabComponent == nullptr && components.size() > numDocsBeforeTabsUsed)
|
||||
{
|
||||
tabComponent.reset (new TabbedComponentInternal());
|
||||
addAndMakeVisible (tabComponent.get());
|
||||
|
||||
auto temp = components;
|
||||
|
||||
for (auto& c : temp)
|
||||
tabComponent->addTab (c->getName(), docColour, c, false);
|
||||
|
||||
resized();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tabComponent != nullptr)
|
||||
tabComponent->addTab (component->getName(), docColour, component, false);
|
||||
else
|
||||
addAndMakeVisible (component);
|
||||
}
|
||||
|
||||
setActiveDocument (component);
|
||||
}
|
||||
|
||||
resized();
|
||||
activeDocumentChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::closeDocumentInternal (Component* component)
|
||||
{
|
||||
// Intellisense warns about component being uninitialised.
|
||||
// I'm not sure how a function argument could be uninitialised.
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6001)
|
||||
|
||||
component->removeComponentListener (this);
|
||||
|
||||
const bool shouldDelete = MultiDocHelpers::shouldDeleteComp (component);
|
||||
component->getProperties().remove ("mdiDocumentDelete_");
|
||||
component->getProperties().remove ("mdiDocumentBkg_");
|
||||
|
||||
if (mode == FloatingWindows)
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
{
|
||||
if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
|
||||
{
|
||||
if (dw->getContentComponent() == component)
|
||||
{
|
||||
std::unique_ptr<MultiDocumentPanelWindow> (dw)->clearContentComponent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldDelete)
|
||||
delete component;
|
||||
|
||||
components.removeFirstMatchingValue (component);
|
||||
|
||||
if (isFullscreenWhenOneDocument() && components.size() == 1)
|
||||
{
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
{
|
||||
std::unique_ptr<MultiDocumentPanelWindow> dw (dynamic_cast<MultiDocumentPanelWindow*> (getChildComponent (i)));
|
||||
|
||||
if (dw != nullptr)
|
||||
dw->clearContentComponent();
|
||||
}
|
||||
|
||||
addAndMakeVisible (components.getFirst());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jassert (components.indexOf (component) >= 0);
|
||||
|
||||
if (tabComponent != nullptr)
|
||||
{
|
||||
for (int i = tabComponent->getNumTabs(); --i >= 0;)
|
||||
if (tabComponent->getTabContentComponent (i) == component)
|
||||
tabComponent->removeTab (i);
|
||||
}
|
||||
else
|
||||
{
|
||||
removeChildComponent (component);
|
||||
}
|
||||
|
||||
if (shouldDelete)
|
||||
delete component;
|
||||
|
||||
if (tabComponent != nullptr && tabComponent->getNumTabs() <= numDocsBeforeTabsUsed)
|
||||
tabComponent.reset();
|
||||
|
||||
components.removeFirstMatchingValue (component);
|
||||
|
||||
if (components.size() > 0 && tabComponent == nullptr)
|
||||
addAndMakeVisible (components.getFirst());
|
||||
}
|
||||
|
||||
resized();
|
||||
|
||||
// This ensures that the active tab is painted properly when a tab is closed!
|
||||
if (auto* activeComponent = getActiveDocument())
|
||||
setActiveDocument (activeComponent);
|
||||
|
||||
activeDocumentChanged();
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool MultiDocumentPanel::closeDocument (Component* component,
|
||||
const bool checkItsOkToCloseFirst)
|
||||
{
|
||||
// Intellisense warns about component being uninitialised.
|
||||
// I'm not sure how a function argument could be uninitialised.
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6001)
|
||||
|
||||
if (component == nullptr)
|
||||
return true;
|
||||
|
||||
if (components.contains (component))
|
||||
{
|
||||
if (checkItsOkToCloseFirst && ! tryToCloseDocument (component))
|
||||
return false;
|
||||
|
||||
closeDocumentInternal (component);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
#endif
|
||||
|
||||
void MultiDocumentPanel::closeDocumentAsync (Component* component,
|
||||
const bool checkItsOkToCloseFirst,
|
||||
std::function<void (bool)> callback)
|
||||
{
|
||||
// Intellisense warns about component being uninitialised.
|
||||
// I'm not sure how a function argument could be uninitialised.
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6001)
|
||||
|
||||
if (component == nullptr)
|
||||
{
|
||||
if (callback != nullptr)
|
||||
callback (true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (components.contains (component))
|
||||
{
|
||||
if (checkItsOkToCloseFirst)
|
||||
{
|
||||
tryToCloseDocumentAsync (component,
|
||||
[parent = SafePointer<MultiDocumentPanel> { this }, component, callback] (bool closedSuccessfully)
|
||||
{
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
if (closedSuccessfully)
|
||||
parent->closeDocumentInternal (component);
|
||||
|
||||
if (callback != nullptr)
|
||||
callback (closedSuccessfully);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
if (callback != nullptr)
|
||||
callback (true);
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
int MultiDocumentPanel::getNumDocuments() const noexcept
|
||||
{
|
||||
return components.size();
|
||||
}
|
||||
|
||||
Component* MultiDocumentPanel::getDocument (const int index) const noexcept
|
||||
{
|
||||
return components [index];
|
||||
}
|
||||
|
||||
Component* MultiDocumentPanel::getActiveDocument() const noexcept
|
||||
{
|
||||
if (mode == FloatingWindows)
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
|
||||
if (dw->isActiveWindow())
|
||||
return dw->getContentComponent();
|
||||
}
|
||||
|
||||
return components.getLast();
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::setActiveDocument (Component* component)
|
||||
{
|
||||
jassert (component != nullptr);
|
||||
|
||||
if (mode == FloatingWindows)
|
||||
{
|
||||
component = getContainerComp (component);
|
||||
|
||||
if (component != nullptr)
|
||||
component->toFront (true);
|
||||
}
|
||||
else if (tabComponent != nullptr)
|
||||
{
|
||||
jassert (components.indexOf (component) >= 0);
|
||||
|
||||
for (int i = tabComponent->getNumTabs(); --i >= 0;)
|
||||
{
|
||||
if (tabComponent->getTabContentComponent (i) == component)
|
||||
{
|
||||
tabComponent->setCurrentTabIndex (i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
component->grabKeyboardFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::activeDocumentChanged()
|
||||
{
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::setMaximumNumDocuments (const int newNumber)
|
||||
{
|
||||
maximumNumDocuments = newNumber;
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::useFullscreenWhenOneDocument (const bool shouldUseTabs)
|
||||
{
|
||||
numDocsBeforeTabsUsed = shouldUseTabs ? 1 : 0;
|
||||
}
|
||||
|
||||
bool MultiDocumentPanel::isFullscreenWhenOneDocument() const noexcept
|
||||
{
|
||||
return numDocsBeforeTabsUsed != 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MultiDocumentPanel::setLayoutMode (const LayoutMode newLayoutMode)
|
||||
{
|
||||
if (mode != newLayoutMode)
|
||||
{
|
||||
mode = newLayoutMode;
|
||||
|
||||
if (mode == FloatingWindows)
|
||||
{
|
||||
tabComponent.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
{
|
||||
std::unique_ptr<MultiDocumentPanelWindow> dw (dynamic_cast<MultiDocumentPanelWindow*> (getChildComponent (i)));
|
||||
|
||||
if (dw != nullptr)
|
||||
{
|
||||
dw->getContentComponent()->getProperties().set ("mdiDocumentPos_", dw->getWindowStateAsString());
|
||||
dw->clearContentComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resized();
|
||||
|
||||
auto tempComps = components;
|
||||
components.clear();
|
||||
|
||||
for (auto* c : tempComps)
|
||||
addDocument (c,
|
||||
Colour ((uint32) static_cast<int> (c->getProperties().getWithDefault ("mdiDocumentBkg_", (int) Colours::white.getARGB()))),
|
||||
MultiDocHelpers::shouldDeleteComp (c));
|
||||
}
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::setBackgroundColour (Colour newBackgroundColour)
|
||||
{
|
||||
if (backgroundColour != newBackgroundColour)
|
||||
{
|
||||
backgroundColour = newBackgroundColour;
|
||||
setOpaque (newBackgroundColour.isOpaque());
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MultiDocumentPanel::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (backgroundColour);
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::resized()
|
||||
{
|
||||
if (mode == MaximisedWindowsWithTabs || components.size() == numDocsBeforeTabsUsed)
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
child->setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
setWantsKeyboardFocus (components.size() == 0);
|
||||
}
|
||||
|
||||
Component* MultiDocumentPanel::getContainerComp (Component* c) const
|
||||
{
|
||||
if (mode == FloatingWindows)
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
|
||||
if (dw->getContentComponent() == c)
|
||||
return dw;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::componentNameChanged (Component&)
|
||||
{
|
||||
if (mode == FloatingWindows)
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
|
||||
dw->setName (dw->getContentComponent()->getName());
|
||||
}
|
||||
else if (tabComponent != nullptr)
|
||||
{
|
||||
for (int i = tabComponent->getNumTabs(); --i >= 0;)
|
||||
tabComponent->setTabName (i, tabComponent->getTabContentComponent (i)->getName());
|
||||
}
|
||||
}
|
||||
|
||||
void MultiDocumentPanel::updateOrder()
|
||||
{
|
||||
auto oldList = components;
|
||||
|
||||
if (mode == FloatingWindows)
|
||||
{
|
||||
components.clear();
|
||||
|
||||
for (auto* child : getChildren())
|
||||
if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
|
||||
components.add (dw->getContentComponent());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tabComponent != nullptr)
|
||||
{
|
||||
if (auto* current = tabComponent->getCurrentContentComponent())
|
||||
{
|
||||
components.removeFirstMatchingValue (current);
|
||||
components.add (current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (components != oldList)
|
||||
activeDocumentChanged();
|
||||
}
|
||||
|
||||
} // namespace juce
|
376
deps/juce/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h
vendored
Normal file
376
deps/juce/modules/juce_gui_basics/layout/juce_MultiDocumentPanel.h
vendored
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class MultiDocumentPanel;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This is a derivative of DocumentWindow that is used inside a MultiDocumentPanel
|
||||
component.
|
||||
|
||||
It's like a normal DocumentWindow but has some extra functionality to make sure
|
||||
everything works nicely inside a MultiDocumentPanel.
|
||||
|
||||
@see MultiDocumentPanel
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API MultiDocumentPanelWindow : public DocumentWindow
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
MultiDocumentPanelWindow (Colour backgroundColour);
|
||||
|
||||
/** Destructor. */
|
||||
~MultiDocumentPanelWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void maximiseButtonPressed() override;
|
||||
/** @internal */
|
||||
void closeButtonPressed() override;
|
||||
/** @internal */
|
||||
void activeWindowStatusChanged() override;
|
||||
/** @internal */
|
||||
void broughtToFront() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void updateOrder();
|
||||
MultiDocumentPanel* getOwner() const noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiDocumentPanelWindow)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that contains a set of other components either in floating windows
|
||||
or tabs.
|
||||
|
||||
This acts as a panel that can be used to hold a set of open document windows, with
|
||||
different layout modes.
|
||||
|
||||
Use addDocument() and closeDocument() to add or remove components from the
|
||||
panel - never use any of the Component methods to access the panel's child
|
||||
components directly, as these are managed internally.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API MultiDocumentPanel : public Component,
|
||||
private ComponentListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty panel.
|
||||
|
||||
Use addDocument() and closeDocument() to add or remove components from the
|
||||
panel - never use any of the Component methods to access the panel's child
|
||||
components directly, as these are managed internally.
|
||||
*/
|
||||
MultiDocumentPanel();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
When deleted, this will call closeAllDocuments (false) to make sure all its
|
||||
components are deleted. If you need to make sure all documents are saved
|
||||
before closing, then you should call closeAllDocuments (true) and check that
|
||||
it returns true before deleting the panel.
|
||||
*/
|
||||
~MultiDocumentPanel() override;
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Tries to close all the documents.
|
||||
|
||||
If checkItsOkToCloseFirst is true, then the tryToCloseDocument() method will
|
||||
be called for each open document, and any of these calls fails, this method
|
||||
will stop and return false, leaving some documents still open.
|
||||
|
||||
If checkItsOkToCloseFirst is false, then all documents will be closed
|
||||
unconditionally.
|
||||
|
||||
@see closeDocument
|
||||
*/
|
||||
bool closeAllDocuments (bool checkItsOkToCloseFirst);
|
||||
#endif
|
||||
|
||||
/** Tries to close all the documents.
|
||||
|
||||
If checkItsOkToCloseFirst is true, then the tryToCloseDocumentAsync() method will
|
||||
be called for each open document, and any of these calls fails, this method
|
||||
will stop and provide an argument of false to the callback, leaving some documents
|
||||
still open.
|
||||
|
||||
If checkItsOkToCloseFirst is false, then all documents will be closed
|
||||
unconditionally.
|
||||
|
||||
@see closeDocument
|
||||
*/
|
||||
void closeAllDocumentsAsync (bool checkItsOkToCloseFirst,
|
||||
std::function<void (bool)> callback);
|
||||
|
||||
/** Adds a document component to the panel.
|
||||
|
||||
If the number of documents would exceed the limit set by setMaximumNumDocuments() then
|
||||
this will fail and return false. (If it does fail, the component passed-in will not be
|
||||
deleted, even if deleteWhenRemoved was set to true).
|
||||
|
||||
The MultiDocumentPanel will deal with creating a window border to go around your component,
|
||||
so just pass in the bare content component here, no need to give it a ResizableWindow
|
||||
or DocumentWindow.
|
||||
|
||||
@param component the component to add
|
||||
@param backgroundColour the background colour to use to fill the component's
|
||||
window or tab
|
||||
@param deleteWhenRemoved if true, then when the component is removed by closeDocument()
|
||||
or closeAllDocuments(), then it will be deleted. If false, then
|
||||
the caller must handle the component's deletion
|
||||
*/
|
||||
bool addDocument (Component* component,
|
||||
Colour backgroundColour,
|
||||
bool deleteWhenRemoved);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Closes one of the documents.
|
||||
|
||||
If checkItsOkToCloseFirst is true, then the tryToCloseDocument() method will
|
||||
be called, and if it fails, this method will return false without closing the
|
||||
document.
|
||||
|
||||
If checkItsOkToCloseFirst is false, then the documents will be closed
|
||||
unconditionally.
|
||||
|
||||
The component will be deleted if the deleteWhenRemoved parameter was set to
|
||||
true when it was added with addDocument.
|
||||
|
||||
@see addDocument, closeAllDocuments
|
||||
*/
|
||||
bool closeDocument (Component* component,
|
||||
bool checkItsOkToCloseFirst);
|
||||
#endif
|
||||
|
||||
/** Closes one of the documents.
|
||||
|
||||
If checkItsOkToCloseFirst is true, then the tryToCloseDocumentAsync() method will
|
||||
be called, and if it fails, this method will call the callback with a false
|
||||
argument without closing the document.
|
||||
|
||||
If checkItsOkToCloseFirst is false, then the documents will be closed
|
||||
unconditionally.
|
||||
|
||||
The component will be deleted if the deleteWhenRemoved parameter was set to
|
||||
true when it was added with addDocument.
|
||||
|
||||
@see addDocument, closeAllDocuments
|
||||
*/
|
||||
void closeDocumentAsync (Component* component,
|
||||
bool checkItsOkToCloseFirst,
|
||||
std::function<void (bool)> callback);
|
||||
|
||||
/** Returns the number of open document windows.
|
||||
|
||||
@see getDocument
|
||||
*/
|
||||
int getNumDocuments() const noexcept;
|
||||
|
||||
/** Returns one of the open documents.
|
||||
|
||||
The order of the documents in this array may change when they are added, removed
|
||||
or moved around.
|
||||
|
||||
@see getNumDocuments
|
||||
*/
|
||||
Component* getDocument (int index) const noexcept;
|
||||
|
||||
/** Returns the document component that is currently focused or on top.
|
||||
|
||||
If currently using floating windows, then this will be the component in the currently
|
||||
active window, or the top component if none are active.
|
||||
|
||||
If it's currently in tabbed mode, then it'll return the component in the active tab.
|
||||
|
||||
@see setActiveDocument
|
||||
*/
|
||||
Component* getActiveDocument() const noexcept;
|
||||
|
||||
/** Makes one of the components active and brings it to the top.
|
||||
|
||||
@see getActiveDocument
|
||||
*/
|
||||
void setActiveDocument (Component* component);
|
||||
|
||||
/** Callback which gets invoked when the currently-active document changes. */
|
||||
virtual void activeDocumentChanged();
|
||||
|
||||
/** Sets a limit on how many windows can be open at once.
|
||||
|
||||
If this is zero or less there's no limit (the default). addDocument() will fail
|
||||
if this number is exceeded.
|
||||
*/
|
||||
void setMaximumNumDocuments (int maximumNumDocuments);
|
||||
|
||||
/** Sets an option to make the document fullscreen if there's only one document open.
|
||||
|
||||
If set to true, then if there's only one document, it'll fill the whole of this
|
||||
component without tabs or a window border. If false, then tabs or a window
|
||||
will always be shown, even if there's only one document. If there's more than
|
||||
one document open, then this option makes no difference.
|
||||
*/
|
||||
void useFullscreenWhenOneDocument (bool shouldUseTabs);
|
||||
|
||||
/** Returns the result of the last time useFullscreenWhenOneDocument() was called.
|
||||
*/
|
||||
bool isFullscreenWhenOneDocument() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The different layout modes available. */
|
||||
enum LayoutMode
|
||||
{
|
||||
FloatingWindows, /**< In this mode, there are overlapping DocumentWindow components for each document. */
|
||||
MaximisedWindowsWithTabs /**< In this mode, a TabbedComponent is used to show one document at a time. */
|
||||
};
|
||||
|
||||
/** Changes the panel's mode.
|
||||
|
||||
@see LayoutMode, getLayoutMode
|
||||
*/
|
||||
void setLayoutMode (LayoutMode newLayoutMode);
|
||||
|
||||
/** Returns the current layout mode. */
|
||||
LayoutMode getLayoutMode() const noexcept { return mode; }
|
||||
|
||||
/** Sets the background colour for the whole panel.
|
||||
|
||||
Each document has its own background colour, but this is the one used to fill the areas
|
||||
behind them.
|
||||
*/
|
||||
void setBackgroundColour (Colour newBackgroundColour);
|
||||
|
||||
/** Returns the current background colour.
|
||||
|
||||
@see setBackgroundColour
|
||||
*/
|
||||
Colour getBackgroundColour() const noexcept { return backgroundColour; }
|
||||
|
||||
/** If the panel is being used in tabbed mode, this returns the TabbedComponent that's involved. */
|
||||
TabbedComponent* getCurrentTabbedComponent() const noexcept { return tabComponent.get(); }
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** A subclass must override this to say whether its currently ok for a document
|
||||
to be closed.
|
||||
|
||||
This method is called by closeDocument() and closeAllDocuments() to indicate that
|
||||
a document should be saved if possible, ready for it to be closed.
|
||||
|
||||
If this method returns true, then it means the document is ok and can be closed.
|
||||
|
||||
If it returns false, then it means that the closeDocument() method should stop
|
||||
and not close.
|
||||
|
||||
Normally, you'd use this method to ask the user if they want to save any changes,
|
||||
then return true if the save operation went ok. If the user cancelled the save
|
||||
operation you could return false here to abort the close operation.
|
||||
|
||||
If your component is based on the FileBasedDocument class, then you'd probably want
|
||||
to call FileBasedDocument::saveIfNeededAndUserAgrees() and return true if this returned
|
||||
FileBasedDocument::savedOk
|
||||
|
||||
@see closeDocument, FileBasedDocument::saveIfNeededAndUserAgrees()
|
||||
*/
|
||||
virtual bool tryToCloseDocument (Component* component);
|
||||
#endif
|
||||
|
||||
/** A subclass must override this to say whether its currently ok for a document
|
||||
to be closed.
|
||||
|
||||
This method is called by closeDocumentAsync() and closeAllDocumentsAsync()
|
||||
to indicate that a document should be saved if possible, ready for it to be closed.
|
||||
|
||||
If the callback is called with a true argument, then it means the document is ok
|
||||
and can be closed.
|
||||
|
||||
If the callback is called with a false argument, then it means that the
|
||||
closeDocumentAsync() method should stop and not close.
|
||||
|
||||
Normally, you'd use this method to ask the user if they want to save any changes,
|
||||
then return true if the save operation went ok. If the user cancelled the save
|
||||
operation you could give a value of false to the callback to abort the close operation.
|
||||
|
||||
If your component is based on the FileBasedDocument class, then you'd probably want
|
||||
to call FileBasedDocument::saveIfNeededAndUserAgreesAsync() and call the calback with
|
||||
true if this returned FileBasedDocument::savedOk.
|
||||
|
||||
@see closeDocumentAsync, FileBasedDocument::saveIfNeededAndUserAgreesAsync()
|
||||
*/
|
||||
virtual void tryToCloseDocumentAsync (Component* component, std::function<void (bool)> callback) = 0;
|
||||
|
||||
/** Creates a new window to be used for a document.
|
||||
|
||||
The default implementation of this just returns a basic MultiDocumentPanelWindow object,
|
||||
but you might want to override it to return a custom component.
|
||||
*/
|
||||
virtual MultiDocumentPanelWindow* createNewDocumentWindow();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void componentNameChanged (Component&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void closeDocumentInternal (Component*);
|
||||
static void closeLastDocumentRecursive (SafePointer<MultiDocumentPanel>,
|
||||
bool,
|
||||
std::function<void (bool)>);
|
||||
|
||||
//==============================================================================
|
||||
struct TabbedComponentInternal;
|
||||
friend class MultiDocumentPanelWindow;
|
||||
|
||||
Component* getContainerComp (Component*) const;
|
||||
void updateOrder();
|
||||
void addWindow (Component*);
|
||||
|
||||
LayoutMode mode = MaximisedWindowsWithTabs;
|
||||
Array<Component*> components;
|
||||
std::unique_ptr<TabbedComponent> tabComponent;
|
||||
Colour backgroundColour { Colours::lightblue };
|
||||
int maximumNumDocuments = 0, numDocsBeforeTabsUsed = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiDocumentPanel)
|
||||
};
|
||||
|
||||
} // namespace juce
|
195
deps/juce/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp
vendored
Normal file
195
deps/juce/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ResizableBorderComponent::Zone::Zone() noexcept {}
|
||||
ResizableBorderComponent::Zone::Zone (int zoneFlags) noexcept : zone (zoneFlags) {}
|
||||
ResizableBorderComponent::Zone::Zone (const ResizableBorderComponent::Zone& other) noexcept : zone (other.zone) {}
|
||||
|
||||
ResizableBorderComponent::Zone& ResizableBorderComponent::Zone::operator= (const ResizableBorderComponent::Zone& other) noexcept
|
||||
{
|
||||
zone = other.zone;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool ResizableBorderComponent::Zone::operator== (const ResizableBorderComponent::Zone& other) const noexcept { return zone == other.zone; }
|
||||
bool ResizableBorderComponent::Zone::operator!= (const ResizableBorderComponent::Zone& other) const noexcept { return zone != other.zone; }
|
||||
|
||||
ResizableBorderComponent::Zone ResizableBorderComponent::Zone::fromPositionOnBorder (Rectangle<int> totalSize,
|
||||
BorderSize<int> border,
|
||||
Point<int> position)
|
||||
{
|
||||
int z = 0;
|
||||
|
||||
if (totalSize.contains (position)
|
||||
&& ! border.subtractedFrom (totalSize).contains (position))
|
||||
{
|
||||
auto minW = jmax (totalSize.getWidth() / 10, jmin (10, totalSize.getWidth() / 3));
|
||||
|
||||
if (position.x < jmax (border.getLeft(), minW) && border.getLeft() > 0)
|
||||
z |= left;
|
||||
else if (position.x >= totalSize.getWidth() - jmax (border.getRight(), minW) && border.getRight() > 0)
|
||||
z |= right;
|
||||
|
||||
auto minH = jmax (totalSize.getHeight() / 10, jmin (10, totalSize.getHeight() / 3));
|
||||
|
||||
if (position.y < jmax (border.getTop(), minH) && border.getTop() > 0)
|
||||
z |= top;
|
||||
else if (position.y >= totalSize.getHeight() - jmax (border.getBottom(), minH) && border.getBottom() > 0)
|
||||
z |= bottom;
|
||||
}
|
||||
|
||||
return Zone (z);
|
||||
}
|
||||
|
||||
MouseCursor ResizableBorderComponent::Zone::getMouseCursor() const noexcept
|
||||
{
|
||||
auto mc = MouseCursor::NormalCursor;
|
||||
|
||||
switch (zone)
|
||||
{
|
||||
case (left | top): mc = MouseCursor::TopLeftCornerResizeCursor; break;
|
||||
case top: mc = MouseCursor::TopEdgeResizeCursor; break;
|
||||
case (right | top): mc = MouseCursor::TopRightCornerResizeCursor; break;
|
||||
case left: mc = MouseCursor::LeftEdgeResizeCursor; break;
|
||||
case right: mc = MouseCursor::RightEdgeResizeCursor; break;
|
||||
case (left | bottom): mc = MouseCursor::BottomLeftCornerResizeCursor; break;
|
||||
case bottom: mc = MouseCursor::BottomEdgeResizeCursor; break;
|
||||
case (right | bottom): mc = MouseCursor::BottomRightCornerResizeCursor; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return mc;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ResizableBorderComponent::ResizableBorderComponent (Component* componentToResize,
|
||||
ComponentBoundsConstrainer* boundsConstrainer)
|
||||
: component (componentToResize),
|
||||
constrainer (boundsConstrainer),
|
||||
borderSize (5)
|
||||
{
|
||||
}
|
||||
|
||||
ResizableBorderComponent::~ResizableBorderComponent() = default;
|
||||
|
||||
//==============================================================================
|
||||
void ResizableBorderComponent::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawResizableFrame (g, getWidth(), getHeight(), borderSize);
|
||||
}
|
||||
|
||||
void ResizableBorderComponent::mouseEnter (const MouseEvent& e)
|
||||
{
|
||||
updateMouseZone (e);
|
||||
}
|
||||
|
||||
void ResizableBorderComponent::mouseMove (const MouseEvent& e)
|
||||
{
|
||||
updateMouseZone (e);
|
||||
}
|
||||
|
||||
void ResizableBorderComponent::mouseDown (const MouseEvent& e)
|
||||
{
|
||||
if (component == nullptr)
|
||||
{
|
||||
jassertfalse; // You've deleted the component that this resizer was supposed to be using!
|
||||
return;
|
||||
}
|
||||
|
||||
updateMouseZone (e);
|
||||
|
||||
originalBounds = component->getBounds();
|
||||
|
||||
if (constrainer != nullptr)
|
||||
constrainer->resizeStart();
|
||||
}
|
||||
|
||||
void ResizableBorderComponent::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
if (component == nullptr)
|
||||
{
|
||||
jassertfalse; // You've deleted the component that this resizer was supposed to be using!
|
||||
return;
|
||||
}
|
||||
|
||||
auto newBounds = mouseZone.resizeRectangleBy (originalBounds, e.getOffsetFromDragStart());
|
||||
|
||||
if (constrainer != nullptr)
|
||||
{
|
||||
constrainer->setBoundsForComponent (component, newBounds,
|
||||
mouseZone.isDraggingTopEdge(),
|
||||
mouseZone.isDraggingLeftEdge(),
|
||||
mouseZone.isDraggingBottomEdge(),
|
||||
mouseZone.isDraggingRightEdge());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto* p = component->getPositioner())
|
||||
p->applyNewBounds (newBounds);
|
||||
else
|
||||
component->setBounds (newBounds);
|
||||
}
|
||||
}
|
||||
|
||||
void ResizableBorderComponent::mouseUp (const MouseEvent&)
|
||||
{
|
||||
if (constrainer != nullptr)
|
||||
constrainer->resizeEnd();
|
||||
}
|
||||
|
||||
bool ResizableBorderComponent::hitTest (int x, int y)
|
||||
{
|
||||
return ! borderSize.subtractedFrom (getLocalBounds()).contains (x, y);
|
||||
}
|
||||
|
||||
void ResizableBorderComponent::setBorderThickness (BorderSize<int> newBorderSize)
|
||||
{
|
||||
if (borderSize != newBorderSize)
|
||||
{
|
||||
borderSize = newBorderSize;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
BorderSize<int> ResizableBorderComponent::getBorderThickness() const
|
||||
{
|
||||
return borderSize;
|
||||
}
|
||||
|
||||
void ResizableBorderComponent::updateMouseZone (const MouseEvent& e)
|
||||
{
|
||||
auto newZone = Zone::fromPositionOnBorder (getLocalBounds(), borderSize, e.getPosition());
|
||||
|
||||
if (mouseZone != newZone)
|
||||
{
|
||||
mouseZone = newZone;
|
||||
setMouseCursor (newZone.getMouseCursor());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
196
deps/juce/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h
vendored
Normal file
196
deps/juce/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that resizes its parent component when dragged.
|
||||
|
||||
This component forms a frame around the edge of a component, allowing it to
|
||||
be dragged by the edges or corners to resize it - like the way windows are
|
||||
resized in MSWindows or Linux.
|
||||
|
||||
To use it, just add it to your component, making it fill the entire parent component
|
||||
(there's a mouse hit-test that only traps mouse-events which land around the
|
||||
edge of the component, so it's even ok to put it on top of any other components
|
||||
you're using). Make sure you rescale the resizer component to fill the parent
|
||||
each time the parent's size changes.
|
||||
|
||||
@see ResizableCornerComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ResizableBorderComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a resizer.
|
||||
|
||||
Pass in the target component which you want to be resized when this one is
|
||||
dragged.
|
||||
|
||||
The target component will usually be a parent of the resizer component, but this
|
||||
isn't mandatory.
|
||||
|
||||
Remember that when the target component is resized, it'll need to move and
|
||||
resize this component to keep it in place, as this won't happen automatically.
|
||||
|
||||
If the constrainer parameter is not a nullptr, then this object will be used to
|
||||
enforce limits on the size and position that the component can be stretched to.
|
||||
Make sure that the constrainer isn't deleted while still in use by this object.
|
||||
|
||||
@see ComponentBoundsConstrainer
|
||||
*/
|
||||
ResizableBorderComponent (Component* componentToResize,
|
||||
ComponentBoundsConstrainer* constrainer);
|
||||
|
||||
/** Destructor. */
|
||||
~ResizableBorderComponent() override;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Specifies how many pixels wide the draggable edges of this component are.
|
||||
|
||||
@see getBorderThickness
|
||||
*/
|
||||
void setBorderThickness (BorderSize<int> newBorderSize);
|
||||
|
||||
/** Returns the number of pixels wide that the draggable edges of this component are.
|
||||
|
||||
@see setBorderThickness
|
||||
*/
|
||||
BorderSize<int> getBorderThickness() const;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Represents the different sections of a resizable border, which allow it to
|
||||
resized in different ways.
|
||||
*/
|
||||
class Zone
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum Zones
|
||||
{
|
||||
centre = 0,
|
||||
left = 1,
|
||||
top = 2,
|
||||
right = 4,
|
||||
bottom = 8
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a Zone from a combination of the flags in zoneFlags. */
|
||||
explicit Zone (int zoneFlags) noexcept;
|
||||
|
||||
Zone() noexcept;
|
||||
Zone (const Zone&) noexcept;
|
||||
Zone& operator= (const Zone&) noexcept;
|
||||
|
||||
bool operator== (const Zone&) const noexcept;
|
||||
bool operator!= (const Zone&) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Given a point within a rectangle with a resizable border, this returns the
|
||||
zone that the point lies within.
|
||||
*/
|
||||
static Zone fromPositionOnBorder (Rectangle<int> totalSize,
|
||||
BorderSize<int> border,
|
||||
Point<int> position);
|
||||
|
||||
/** Returns an appropriate mouse-cursor for this resize zone. */
|
||||
MouseCursor getMouseCursor() const noexcept;
|
||||
|
||||
/** Returns true if dragging this zone will move the enire object without resizing it. */
|
||||
bool isDraggingWholeObject() const noexcept { return zone == centre; }
|
||||
/** Returns true if dragging this zone will move the object's left edge. */
|
||||
bool isDraggingLeftEdge() const noexcept { return (zone & left) != 0; }
|
||||
/** Returns true if dragging this zone will move the object's right edge. */
|
||||
bool isDraggingRightEdge() const noexcept { return (zone & right) != 0; }
|
||||
/** Returns true if dragging this zone will move the object's top edge. */
|
||||
bool isDraggingTopEdge() const noexcept { return (zone & top) != 0; }
|
||||
/** Returns true if dragging this zone will move the object's bottom edge. */
|
||||
bool isDraggingBottomEdge() const noexcept { return (zone & bottom) != 0; }
|
||||
|
||||
/** Resizes this rectangle by the given amount, moving just the edges that this zone
|
||||
applies to.
|
||||
*/
|
||||
template <typename ValueType>
|
||||
Rectangle<ValueType> resizeRectangleBy (Rectangle<ValueType> original,
|
||||
const Point<ValueType>& distance) const noexcept
|
||||
{
|
||||
if (isDraggingWholeObject())
|
||||
return original + distance;
|
||||
|
||||
if (isDraggingLeftEdge()) original.setLeft (jmin (original.getRight(), original.getX() + distance.x));
|
||||
if (isDraggingRightEdge()) original.setWidth (jmax (ValueType(), original.getWidth() + distance.x));
|
||||
if (isDraggingTopEdge()) original.setTop (jmin (original.getBottom(), original.getY() + distance.y));
|
||||
if (isDraggingBottomEdge()) original.setHeight (jmax (ValueType(), original.getHeight() + distance.y));
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
/** Returns the raw flags for this zone. */
|
||||
int getZoneFlags() const noexcept { return zone; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int zone = centre;
|
||||
};
|
||||
|
||||
/** Returns the zone in which the mouse was last seen. */
|
||||
Zone getCurrentZone() const noexcept { return mouseZone; }
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void mouseEnter (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseMove (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
|
||||
private:
|
||||
WeakReference<Component> component;
|
||||
ComponentBoundsConstrainer* constrainer;
|
||||
BorderSize<int> borderSize;
|
||||
Rectangle<int> originalBounds;
|
||||
Zone mouseZone;
|
||||
|
||||
void updateMouseZone (const MouseEvent&);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableBorderComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
96
deps/juce/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp
vendored
Normal file
96
deps/juce/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ResizableCornerComponent::ResizableCornerComponent (Component* componentToResize,
|
||||
ComponentBoundsConstrainer* boundsConstrainer)
|
||||
: component (componentToResize),
|
||||
constrainer (boundsConstrainer)
|
||||
{
|
||||
setRepaintsOnMouseActivity (true);
|
||||
setMouseCursor (MouseCursor::BottomRightCornerResizeCursor);
|
||||
}
|
||||
|
||||
ResizableCornerComponent::~ResizableCornerComponent() = default;
|
||||
|
||||
//==============================================================================
|
||||
void ResizableCornerComponent::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawCornerResizer (g, getWidth(), getHeight(),
|
||||
isMouseOverOrDragging(),
|
||||
isMouseButtonDown());
|
||||
}
|
||||
|
||||
void ResizableCornerComponent::mouseDown (const MouseEvent&)
|
||||
{
|
||||
if (component == nullptr)
|
||||
{
|
||||
jassertfalse; // You've deleted the component that this resizer is supposed to be controlling!
|
||||
return;
|
||||
}
|
||||
|
||||
originalBounds = component->getBounds();
|
||||
|
||||
if (constrainer != nullptr)
|
||||
constrainer->resizeStart();
|
||||
}
|
||||
|
||||
void ResizableCornerComponent::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
if (component == nullptr)
|
||||
{
|
||||
jassertfalse; // You've deleted the component that this resizer is supposed to be controlling!
|
||||
return;
|
||||
}
|
||||
|
||||
auto r = originalBounds.withSize (originalBounds.getWidth() + e.getDistanceFromDragStartX(),
|
||||
originalBounds.getHeight() + e.getDistanceFromDragStartY());
|
||||
|
||||
if (constrainer != nullptr)
|
||||
constrainer->setBoundsForComponent (component, r, false, false, true, true);
|
||||
else if (auto pos = component->getPositioner())
|
||||
pos->applyNewBounds (r);
|
||||
else
|
||||
component->setBounds (r);
|
||||
}
|
||||
|
||||
void ResizableCornerComponent::mouseUp (const MouseEvent&)
|
||||
{
|
||||
if (constrainer != nullptr)
|
||||
constrainer->resizeEnd();
|
||||
}
|
||||
|
||||
bool ResizableCornerComponent::hitTest (int x, int y)
|
||||
{
|
||||
if (getWidth() <= 0)
|
||||
return false;
|
||||
|
||||
auto yAtX = getHeight() - (getHeight() * x / getWidth());
|
||||
return y >= yAtX - getHeight() / 4;
|
||||
}
|
||||
|
||||
} // namespace juce
|
92
deps/juce/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.h
vendored
Normal file
92
deps/juce/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.h
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** A component that resizes a parent component when dragged.
|
||||
|
||||
This is the small triangular stripey resizer component you get in the bottom-right
|
||||
of windows (more commonly on the Mac than Windows). Put one in the corner of
|
||||
a larger component and it will automatically resize its parent when it gets dragged
|
||||
around.
|
||||
|
||||
@see ResizableBorderComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ResizableCornerComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a resizer.
|
||||
|
||||
Pass in the target component which you want to be resized when this one is
|
||||
dragged.
|
||||
|
||||
The target component will usually be a parent of the resizer component, but this
|
||||
isn't mandatory.
|
||||
|
||||
Remember that when the target component is resized, it'll need to move and
|
||||
resize this component to keep it in place, as this won't happen automatically.
|
||||
|
||||
If a constrainer object is provided, then this object will be used to enforce
|
||||
limits on the size and position that the component can be stretched to. Make sure
|
||||
that the constrainer isn't deleted while still in use by this object. If you
|
||||
pass a nullptr in here, no limits will be put on the sizes it can be stretched to.
|
||||
|
||||
@see ComponentBoundsConstrainer
|
||||
*/
|
||||
ResizableCornerComponent (Component* componentToResize,
|
||||
ComponentBoundsConstrainer* constrainer);
|
||||
|
||||
/** Destructor. */
|
||||
~ResizableCornerComponent() override;
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
WeakReference<Component> component;
|
||||
ComponentBoundsConstrainer* constrainer;
|
||||
Rectangle<int> originalBounds;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableCornerComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
111
deps/juce/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp
vendored
Normal file
111
deps/juce/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ResizableEdgeComponent::ResizableEdgeComponent (Component* componentToResize,
|
||||
ComponentBoundsConstrainer* boundsConstrainer,
|
||||
Edge e)
|
||||
: component (componentToResize),
|
||||
constrainer (boundsConstrainer),
|
||||
edge (e)
|
||||
{
|
||||
setRepaintsOnMouseActivity (true);
|
||||
setMouseCursor (isVertical() ? MouseCursor::LeftRightResizeCursor
|
||||
: MouseCursor::UpDownResizeCursor);
|
||||
}
|
||||
|
||||
ResizableEdgeComponent::~ResizableEdgeComponent() = default;
|
||||
|
||||
//==============================================================================
|
||||
bool ResizableEdgeComponent::isVertical() const noexcept
|
||||
{
|
||||
return edge == leftEdge || edge == rightEdge;
|
||||
}
|
||||
|
||||
void ResizableEdgeComponent::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawStretchableLayoutResizerBar (g, getWidth(), getHeight(), isVertical(),
|
||||
isMouseOver(), isMouseButtonDown());
|
||||
}
|
||||
|
||||
void ResizableEdgeComponent::mouseDown (const MouseEvent&)
|
||||
{
|
||||
if (component == nullptr)
|
||||
{
|
||||
jassertfalse; // You've deleted the component that this resizer was supposed to be using!
|
||||
return;
|
||||
}
|
||||
|
||||
originalBounds = component->getBounds();
|
||||
|
||||
if (constrainer != nullptr)
|
||||
constrainer->resizeStart();
|
||||
}
|
||||
|
||||
void ResizableEdgeComponent::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
if (component == nullptr)
|
||||
{
|
||||
jassertfalse; // You've deleted the component that this resizer was supposed to be using!
|
||||
return;
|
||||
}
|
||||
|
||||
auto newBounds = originalBounds;
|
||||
|
||||
switch (edge)
|
||||
{
|
||||
case leftEdge: newBounds.setLeft (jmin (newBounds.getRight(), newBounds.getX() + e.getDistanceFromDragStartX())); break;
|
||||
case rightEdge: newBounds.setWidth (jmax (0, newBounds.getWidth() + e.getDistanceFromDragStartX())); break;
|
||||
case topEdge: newBounds.setTop (jmin (newBounds.getBottom(), newBounds.getY() + e.getDistanceFromDragStartY())); break;
|
||||
case bottomEdge: newBounds.setHeight (jmax (0, newBounds.getHeight() + e.getDistanceFromDragStartY())); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
|
||||
if (constrainer != nullptr)
|
||||
{
|
||||
constrainer->setBoundsForComponent (component, newBounds,
|
||||
edge == topEdge,
|
||||
edge == leftEdge,
|
||||
edge == bottomEdge,
|
||||
edge == rightEdge);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto* p = component->getPositioner())
|
||||
p->applyNewBounds (newBounds);
|
||||
else
|
||||
component->setBounds (newBounds);
|
||||
}
|
||||
}
|
||||
|
||||
void ResizableEdgeComponent::mouseUp (const MouseEvent&)
|
||||
{
|
||||
if (constrainer != nullptr)
|
||||
constrainer->resizeEnd();
|
||||
}
|
||||
|
||||
} // namespace juce
|
100
deps/juce/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.h
vendored
Normal file
100
deps/juce/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.h
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that resizes its parent component when dragged.
|
||||
|
||||
This component forms a bar along one edge of a component, allowing it to
|
||||
be dragged by that edges to resize it.
|
||||
|
||||
To use it, just add it to your component, positioning it along the appropriate
|
||||
edge. Make sure you reposition the resizer component each time the parent's size
|
||||
changes, to keep it in the correct position.
|
||||
|
||||
@see ResizableBorderComponent, ResizableCornerComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ResizableEdgeComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum Edge
|
||||
{
|
||||
leftEdge, /**< Indicates a vertical bar that can be dragged left/right to move the component's left-hand edge. */
|
||||
rightEdge, /**< Indicates a vertical bar that can be dragged left/right to move the component's right-hand edge. */
|
||||
topEdge, /**< Indicates a horizontal bar that can be dragged up/down to move the top of the component. */
|
||||
bottomEdge /**< Indicates a horizontal bar that can be dragged up/down to move the bottom of the component. */
|
||||
};
|
||||
|
||||
/** Creates a resizer bar.
|
||||
|
||||
Pass in the target component which you want to be resized when this one is
|
||||
dragged. The target component will usually be this component's parent, but this
|
||||
isn't mandatory.
|
||||
|
||||
Remember that when the target component is resized, it'll need to move and
|
||||
resize this component to keep it in place, as this won't happen automatically.
|
||||
|
||||
If the constrainer parameter is not a nullptr, then this object will be used to
|
||||
enforce limits on the size and position that the component can be stretched to.
|
||||
Make sure that the constrainer isn't deleted while still in use by this object.
|
||||
|
||||
@see ComponentBoundsConstrainer
|
||||
*/
|
||||
ResizableEdgeComponent (Component* componentToResize,
|
||||
ComponentBoundsConstrainer* constrainer,
|
||||
Edge edgeToResize);
|
||||
|
||||
/** Destructor. */
|
||||
~ResizableEdgeComponent() override;
|
||||
|
||||
bool isVertical() const noexcept;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
|
||||
private:
|
||||
WeakReference<Component> component;
|
||||
ComponentBoundsConstrainer* constrainer;
|
||||
Rectangle<int> originalBounds;
|
||||
const Edge edge;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableEdgeComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
477
deps/juce/modules/juce_gui_basics/layout/juce_ScrollBar.cpp
vendored
Normal file
477
deps/juce/modules/juce_gui_basics/layout/juce_ScrollBar.cpp
vendored
Normal file
@ -0,0 +1,477 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class ScrollBar::ScrollbarButton : public Button
|
||||
{
|
||||
public:
|
||||
ScrollbarButton (int direc, ScrollBar& s)
|
||||
: Button (String()), direction (direc), owner (s)
|
||||
{
|
||||
setWantsKeyboardFocus (false);
|
||||
}
|
||||
|
||||
void paintButton (Graphics& g, bool over, bool down) override
|
||||
{
|
||||
getLookAndFeel().drawScrollbarButton (g, owner, getWidth(), getHeight(),
|
||||
direction, owner.isVertical(), over, down);
|
||||
}
|
||||
|
||||
void clicked() override
|
||||
{
|
||||
owner.moveScrollbarInSteps ((direction == 1 || direction == 2) ? 1 : -1);
|
||||
}
|
||||
|
||||
using Button::clicked;
|
||||
|
||||
int direction;
|
||||
|
||||
private:
|
||||
ScrollBar& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScrollbarButton)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
ScrollBar::ScrollBar (bool shouldBeVertical) : vertical (shouldBeVertical)
|
||||
{
|
||||
setRepaintsOnMouseActivity (true);
|
||||
setFocusContainerType (FocusContainerType::keyboardFocusContainer);
|
||||
}
|
||||
|
||||
ScrollBar::~ScrollBar()
|
||||
{
|
||||
upButton.reset();
|
||||
downButton.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ScrollBar::setRangeLimits (Range<double> newRangeLimit, NotificationType notification)
|
||||
{
|
||||
if (totalRange != newRangeLimit)
|
||||
{
|
||||
totalRange = newRangeLimit;
|
||||
setCurrentRange (visibleRange, notification);
|
||||
updateThumbPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollBar::setRangeLimits (double newMinimum, double newMaximum, NotificationType notification)
|
||||
{
|
||||
jassert (newMaximum >= newMinimum); // these can't be the wrong way round!
|
||||
setRangeLimits (Range<double> (newMinimum, newMaximum), notification);
|
||||
}
|
||||
|
||||
bool ScrollBar::setCurrentRange (Range<double> newRange, NotificationType notification)
|
||||
{
|
||||
auto constrainedRange = totalRange.constrainRange (newRange);
|
||||
|
||||
if (visibleRange != constrainedRange)
|
||||
{
|
||||
visibleRange = constrainedRange;
|
||||
|
||||
updateThumbPosition();
|
||||
|
||||
if (notification != dontSendNotification)
|
||||
triggerAsyncUpdate();
|
||||
|
||||
if (notification == sendNotificationSync)
|
||||
handleUpdateNowIfNeeded();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScrollBar::setCurrentRange (double newStart, double newSize, NotificationType notification)
|
||||
{
|
||||
setCurrentRange (Range<double> (newStart, newStart + newSize), notification);
|
||||
}
|
||||
|
||||
void ScrollBar::setCurrentRangeStart (double newStart, NotificationType notification)
|
||||
{
|
||||
setCurrentRange (visibleRange.movedToStartAt (newStart), notification);
|
||||
}
|
||||
|
||||
void ScrollBar::setSingleStepSize (double newSingleStepSize) noexcept
|
||||
{
|
||||
singleStepSize = newSingleStepSize;
|
||||
}
|
||||
|
||||
bool ScrollBar::moveScrollbarInSteps (int howManySteps, NotificationType notification)
|
||||
{
|
||||
return setCurrentRange (visibleRange + howManySteps * singleStepSize, notification);
|
||||
}
|
||||
|
||||
bool ScrollBar::moveScrollbarInPages (int howManyPages, NotificationType notification)
|
||||
{
|
||||
return setCurrentRange (visibleRange + howManyPages * visibleRange.getLength(), notification);
|
||||
}
|
||||
|
||||
bool ScrollBar::scrollToTop (NotificationType notification)
|
||||
{
|
||||
return setCurrentRange (visibleRange.movedToStartAt (getMinimumRangeLimit()), notification);
|
||||
}
|
||||
|
||||
bool ScrollBar::scrollToBottom (NotificationType notification)
|
||||
{
|
||||
return setCurrentRange (visibleRange.movedToEndAt (getMaximumRangeLimit()), notification);
|
||||
}
|
||||
|
||||
void ScrollBar::setButtonRepeatSpeed (int newInitialDelay,
|
||||
int newRepeatDelay,
|
||||
int newMinimumDelay)
|
||||
{
|
||||
initialDelayInMillisecs = newInitialDelay;
|
||||
repeatDelayInMillisecs = newRepeatDelay;
|
||||
minimumDelayInMillisecs = newMinimumDelay;
|
||||
|
||||
if (upButton != nullptr)
|
||||
{
|
||||
upButton ->setRepeatSpeed (newInitialDelay, newRepeatDelay, newMinimumDelay);
|
||||
downButton->setRepeatSpeed (newInitialDelay, newRepeatDelay, newMinimumDelay);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ScrollBar::addListener (Listener* listener)
|
||||
{
|
||||
listeners.add (listener);
|
||||
}
|
||||
|
||||
void ScrollBar::removeListener (Listener* listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
void ScrollBar::handleAsyncUpdate()
|
||||
{
|
||||
auto start = visibleRange.getStart(); // (need to use a temp variable for VC7 compatibility)
|
||||
listeners.call ([this, start] (Listener& l) { l.scrollBarMoved (this, start); });
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ScrollBar::updateThumbPosition()
|
||||
{
|
||||
auto minimumScrollBarThumbSize = getLookAndFeel().getMinimumScrollbarThumbSize (*this);
|
||||
|
||||
int newThumbSize = roundToInt (totalRange.getLength() > 0 ? (visibleRange.getLength() * thumbAreaSize) / totalRange.getLength()
|
||||
: thumbAreaSize);
|
||||
|
||||
if (newThumbSize < minimumScrollBarThumbSize)
|
||||
newThumbSize = jmin (minimumScrollBarThumbSize, thumbAreaSize - 1);
|
||||
|
||||
if (newThumbSize > thumbAreaSize)
|
||||
newThumbSize = thumbAreaSize;
|
||||
|
||||
int newThumbStart = thumbAreaStart;
|
||||
|
||||
if (totalRange.getLength() > visibleRange.getLength())
|
||||
newThumbStart += roundToInt (((visibleRange.getStart() - totalRange.getStart()) * (thumbAreaSize - newThumbSize))
|
||||
/ (totalRange.getLength() - visibleRange.getLength()));
|
||||
|
||||
Component::setVisible (getVisibility());
|
||||
|
||||
if (thumbStart != newThumbStart || thumbSize != newThumbSize)
|
||||
{
|
||||
auto repaintStart = jmin (thumbStart, newThumbStart) - 4;
|
||||
auto repaintSize = jmax (thumbStart + thumbSize, newThumbStart + newThumbSize) + 8 - repaintStart;
|
||||
|
||||
if (vertical)
|
||||
repaint (0, repaintStart, getWidth(), repaintSize);
|
||||
else
|
||||
repaint (repaintStart, 0, repaintSize, getHeight());
|
||||
|
||||
thumbStart = newThumbStart;
|
||||
thumbSize = newThumbSize;
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollBar::setOrientation (bool shouldBeVertical)
|
||||
{
|
||||
if (vertical != shouldBeVertical)
|
||||
{
|
||||
vertical = shouldBeVertical;
|
||||
|
||||
if (upButton != nullptr)
|
||||
{
|
||||
upButton->direction = vertical ? 0 : 3;
|
||||
downButton->direction = vertical ? 2 : 1;
|
||||
}
|
||||
|
||||
updateThumbPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollBar::setAutoHide (bool shouldHideWhenFullRange)
|
||||
{
|
||||
autohides = shouldHideWhenFullRange;
|
||||
updateThumbPosition();
|
||||
}
|
||||
|
||||
bool ScrollBar::autoHides() const noexcept
|
||||
{
|
||||
return autohides;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ScrollBar::paint (Graphics& g)
|
||||
{
|
||||
if (thumbAreaSize > 0)
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
auto thumb = (thumbAreaSize > lf.getMinimumScrollbarThumbSize (*this))
|
||||
? thumbSize : 0;
|
||||
|
||||
if (vertical)
|
||||
lf.drawScrollbar (g, *this, 0, thumbAreaStart, getWidth(), thumbAreaSize,
|
||||
vertical, thumbStart, thumb, isMouseOver(), isMouseButtonDown());
|
||||
else
|
||||
lf.drawScrollbar (g, *this, thumbAreaStart, 0, thumbAreaSize, getHeight(),
|
||||
vertical, thumbStart, thumb, isMouseOver(), isMouseButtonDown());
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollBar::lookAndFeelChanged()
|
||||
{
|
||||
setComponentEffect (getLookAndFeel().getScrollbarEffect());
|
||||
|
||||
if (isVisible())
|
||||
resized();
|
||||
}
|
||||
|
||||
void ScrollBar::resized()
|
||||
{
|
||||
auto length = vertical ? getHeight() : getWidth();
|
||||
|
||||
auto& lf = getLookAndFeel();
|
||||
bool buttonsVisible = lf.areScrollbarButtonsVisible();
|
||||
int buttonSize = 0;
|
||||
|
||||
if (buttonsVisible)
|
||||
{
|
||||
if (upButton == nullptr)
|
||||
{
|
||||
upButton .reset (new ScrollbarButton (vertical ? 0 : 3, *this));
|
||||
downButton.reset (new ScrollbarButton (vertical ? 2 : 1, *this));
|
||||
|
||||
addAndMakeVisible (upButton.get());
|
||||
addAndMakeVisible (downButton.get());
|
||||
|
||||
setButtonRepeatSpeed (initialDelayInMillisecs, repeatDelayInMillisecs, minimumDelayInMillisecs);
|
||||
}
|
||||
|
||||
buttonSize = jmin (lf.getScrollbarButtonSize (*this), length / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
upButton.reset();
|
||||
downButton.reset();
|
||||
}
|
||||
|
||||
if (length < 32 + lf.getMinimumScrollbarThumbSize (*this))
|
||||
{
|
||||
thumbAreaStart = length / 2;
|
||||
thumbAreaSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbAreaStart = buttonSize;
|
||||
thumbAreaSize = length - 2 * buttonSize;
|
||||
}
|
||||
|
||||
if (upButton != nullptr)
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
|
||||
if (vertical)
|
||||
{
|
||||
upButton->setBounds (r.removeFromTop (buttonSize));
|
||||
downButton->setBounds (r.removeFromBottom (buttonSize));
|
||||
}
|
||||
else
|
||||
{
|
||||
upButton->setBounds (r.removeFromLeft (buttonSize));
|
||||
downButton->setBounds (r.removeFromRight (buttonSize));
|
||||
}
|
||||
}
|
||||
|
||||
updateThumbPosition();
|
||||
}
|
||||
|
||||
void ScrollBar::parentHierarchyChanged()
|
||||
{
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void ScrollBar::mouseDown (const MouseEvent& e)
|
||||
{
|
||||
isDraggingThumb = false;
|
||||
lastMousePos = vertical ? e.y : e.x;
|
||||
dragStartMousePos = lastMousePos;
|
||||
dragStartRange = visibleRange.getStart();
|
||||
|
||||
if (dragStartMousePos < thumbStart)
|
||||
{
|
||||
moveScrollbarInPages (-1);
|
||||
startTimer (400);
|
||||
}
|
||||
else if (dragStartMousePos >= thumbStart + thumbSize)
|
||||
{
|
||||
moveScrollbarInPages (1);
|
||||
startTimer (400);
|
||||
}
|
||||
else
|
||||
{
|
||||
isDraggingThumb = (thumbAreaSize > getLookAndFeel().getMinimumScrollbarThumbSize (*this))
|
||||
&& (thumbAreaSize > thumbSize);
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollBar::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
auto mousePos = vertical ? e.y : e.x;
|
||||
|
||||
if (isDraggingThumb && lastMousePos != mousePos && thumbAreaSize > thumbSize)
|
||||
{
|
||||
auto deltaPixels = mousePos - dragStartMousePos;
|
||||
|
||||
setCurrentRangeStart (dragStartRange
|
||||
+ deltaPixels * (totalRange.getLength() - visibleRange.getLength())
|
||||
/ (thumbAreaSize - thumbSize));
|
||||
}
|
||||
|
||||
lastMousePos = mousePos;
|
||||
}
|
||||
|
||||
void ScrollBar::mouseUp (const MouseEvent&)
|
||||
{
|
||||
isDraggingThumb = false;
|
||||
stopTimer();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void ScrollBar::mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel)
|
||||
{
|
||||
float increment = 10.0f * (vertical ? wheel.deltaY : wheel.deltaX);
|
||||
|
||||
if (increment < 0)
|
||||
increment = jmin (increment, -1.0f);
|
||||
else if (increment > 0)
|
||||
increment = jmax (increment, 1.0f);
|
||||
|
||||
setCurrentRange (visibleRange - singleStepSize * increment);
|
||||
}
|
||||
|
||||
void ScrollBar::timerCallback()
|
||||
{
|
||||
if (isMouseButtonDown())
|
||||
{
|
||||
startTimer (40);
|
||||
|
||||
if (lastMousePos < thumbStart)
|
||||
setCurrentRange (visibleRange - visibleRange.getLength());
|
||||
else if (lastMousePos > thumbStart + thumbSize)
|
||||
setCurrentRangeStart (visibleRange.getEnd());
|
||||
}
|
||||
else
|
||||
{
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
bool ScrollBar::keyPressed (const KeyPress& key)
|
||||
{
|
||||
if (isVisible())
|
||||
{
|
||||
if (key == KeyPress::upKey || key == KeyPress::leftKey) return moveScrollbarInSteps (-1);
|
||||
if (key == KeyPress::downKey || key == KeyPress::rightKey) return moveScrollbarInSteps (1);
|
||||
if (key == KeyPress::pageUpKey) return moveScrollbarInPages (-1);
|
||||
if (key == KeyPress::pageDownKey) return moveScrollbarInPages (1);
|
||||
if (key == KeyPress::homeKey) return scrollToTop();
|
||||
if (key == KeyPress::endKey) return scrollToBottom();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScrollBar::setVisible (bool shouldBeVisible)
|
||||
{
|
||||
if (userVisibilityFlag != shouldBeVisible)
|
||||
{
|
||||
userVisibilityFlag = shouldBeVisible;
|
||||
Component::setVisible (getVisibility());
|
||||
}
|
||||
}
|
||||
|
||||
bool ScrollBar::getVisibility() const noexcept
|
||||
{
|
||||
if (! userVisibilityFlag)
|
||||
return false;
|
||||
|
||||
return (! autohides) || (totalRange.getLength() > visibleRange.getLength()
|
||||
&& visibleRange.getLength() > 0.0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> ScrollBar::createAccessibilityHandler()
|
||||
{
|
||||
class ValueInterface : public AccessibilityRangedNumericValueInterface
|
||||
{
|
||||
public:
|
||||
explicit ValueInterface (ScrollBar& scrollBarToWrap) : scrollBar (scrollBarToWrap) {}
|
||||
|
||||
bool isReadOnly() const override { return false; }
|
||||
|
||||
double getCurrentValue() const override { return scrollBar.getCurrentRangeStart(); }
|
||||
void setValue (double newValue) override { scrollBar.setCurrentRangeStart (newValue); }
|
||||
|
||||
AccessibleValueRange getRange() const override
|
||||
{
|
||||
if (scrollBar.getRangeLimit().isEmpty())
|
||||
return {};
|
||||
|
||||
return { { scrollBar.getMinimumRangeLimit(), scrollBar.getMaximumRangeLimit() },
|
||||
scrollBar.getSingleStepSize() };
|
||||
}
|
||||
|
||||
private:
|
||||
ScrollBar& scrollBar;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueInterface)
|
||||
};
|
||||
|
||||
return std::make_unique<AccessibilityHandler> (*this,
|
||||
AccessibilityRole::scrollBar,
|
||||
AccessibilityActions{},
|
||||
AccessibilityHandler::Interfaces { std::make_unique<ValueInterface> (*this) });
|
||||
}
|
||||
|
||||
} // namespace juce
|
440
deps/juce/modules/juce_gui_basics/layout/juce_ScrollBar.h
vendored
Normal file
440
deps/juce/modules/juce_gui_basics/layout/juce_ScrollBar.h
vendored
Normal file
@ -0,0 +1,440 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A scrollbar component.
|
||||
|
||||
To use a scrollbar, set up its total range using the setRangeLimits() method - this
|
||||
sets the range of values it can represent. Then you can use setCurrentRange() to
|
||||
change the position and size of the scrollbar's 'thumb'.
|
||||
|
||||
Registering a ScrollBar::Listener with the scrollbar will allow you to find out when
|
||||
the user moves it, and you can use the getCurrentRangeStart() to find out where
|
||||
they moved it to.
|
||||
|
||||
The scrollbar will adjust its own visibility according to whether its thumb size
|
||||
allows it to actually be scrolled.
|
||||
|
||||
For most purposes, it's probably easier to use a Viewport or ListBox
|
||||
instead of handling a scrollbar directly.
|
||||
|
||||
@see ScrollBar::Listener
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ScrollBar : public Component,
|
||||
public AsyncUpdater,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a Scrollbar.
|
||||
@param isVertical specifies whether the bar should be a vertical or horizontal
|
||||
*/
|
||||
ScrollBar (bool isVertical);
|
||||
|
||||
/** Destructor. */
|
||||
~ScrollBar() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the scrollbar is vertical, false if it's horizontal. */
|
||||
bool isVertical() const noexcept { return vertical; }
|
||||
|
||||
/** Changes the scrollbar's direction.
|
||||
|
||||
You'll also need to resize the bar appropriately - this just changes its internal
|
||||
layout.
|
||||
|
||||
@param shouldBeVertical true makes it vertical; false makes it horizontal.
|
||||
*/
|
||||
void setOrientation (bool shouldBeVertical);
|
||||
|
||||
/** Tells the scrollbar whether to make itself invisible when not needed.
|
||||
|
||||
The default behaviour is for a scrollbar to become invisible when the thumb
|
||||
fills the whole of its range (i.e. when it can't be moved). Setting this
|
||||
value to false forces the bar to always be visible.
|
||||
@see autoHides()
|
||||
*/
|
||||
void setAutoHide (bool shouldHideWhenFullRange);
|
||||
|
||||
/** Returns true if this scrollbar is set to auto-hide when its thumb is as big
|
||||
as its maximum range.
|
||||
@see setAutoHide
|
||||
*/
|
||||
bool autoHides() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the minimum and maximum values that the bar will move between.
|
||||
|
||||
The bar's thumb will always be constrained so that the entire thumb lies
|
||||
within this range.
|
||||
|
||||
@param newRangeLimit the new range.
|
||||
@param notification whether to send a notification of the change to listeners.
|
||||
A notification will only be sent if the range has changed.
|
||||
|
||||
@see setCurrentRange
|
||||
*/
|
||||
void setRangeLimits (Range<double> newRangeLimit,
|
||||
NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Sets the minimum and maximum values that the bar will move between.
|
||||
|
||||
The bar's thumb will always be constrained so that the entire thumb lies
|
||||
within this range.
|
||||
|
||||
@param minimum the new range minimum.
|
||||
@param maximum the new range maximum.
|
||||
@param notification whether to send a notification of the change to listeners.
|
||||
A notification will only be sent if the range has changed.
|
||||
|
||||
@see setCurrentRange
|
||||
*/
|
||||
void setRangeLimits (double minimum, double maximum,
|
||||
NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Returns the current limits on the thumb position.
|
||||
@see setRangeLimits
|
||||
*/
|
||||
Range<double> getRangeLimit() const noexcept { return totalRange; }
|
||||
|
||||
/** Returns the lower value that the thumb can be set to.
|
||||
|
||||
This is the value set by setRangeLimits().
|
||||
*/
|
||||
double getMinimumRangeLimit() const noexcept { return totalRange.getStart(); }
|
||||
|
||||
/** Returns the upper value that the thumb can be set to.
|
||||
|
||||
This is the value set by setRangeLimits().
|
||||
*/
|
||||
double getMaximumRangeLimit() const noexcept { return totalRange.getEnd(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the position of the scrollbar's 'thumb'.
|
||||
|
||||
This sets both the position and size of the thumb - to just set the position without
|
||||
changing the size, you can use setCurrentRangeStart().
|
||||
|
||||
If this method call actually changes the scrollbar's position, it will trigger an
|
||||
asynchronous call to ScrollBar::Listener::scrollBarMoved() for all the listeners that
|
||||
are registered.
|
||||
|
||||
The notification parameter can be used to optionally send or inhibit a callback to
|
||||
any scrollbar listeners.
|
||||
|
||||
@returns true if the range was changed, or false if nothing was changed.
|
||||
@see getCurrentRange. setCurrentRangeStart
|
||||
*/
|
||||
bool setCurrentRange (Range<double> newRange,
|
||||
NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Changes the position of the scrollbar's 'thumb'.
|
||||
|
||||
This sets both the position and size of the thumb - to just set the position without
|
||||
changing the size, you can use setCurrentRangeStart().
|
||||
|
||||
@param newStart the top (or left) of the thumb, in the range
|
||||
getMinimumRangeLimit() <= newStart <= getMaximumRangeLimit(). If the
|
||||
value is beyond these limits, it will be clipped.
|
||||
@param newSize the size of the thumb, such that
|
||||
getMinimumRangeLimit() <= newStart + newSize <= getMaximumRangeLimit(). If the
|
||||
size is beyond these limits, it will be clipped.
|
||||
@param notification specifies if and how a callback should be made to any listeners
|
||||
if the range actually changes
|
||||
@see setCurrentRangeStart, getCurrentRangeStart, getCurrentRangeSize
|
||||
*/
|
||||
void setCurrentRange (double newStart, double newSize,
|
||||
NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Moves the bar's thumb position.
|
||||
|
||||
This will move the thumb position without changing the thumb size. Note
|
||||
that the maximum thumb start position is (getMaximumRangeLimit() - getCurrentRangeSize()).
|
||||
|
||||
If this method call actually changes the scrollbar's position, it will trigger an
|
||||
asynchronous call to ScrollBar::Listener::scrollBarMoved() for all the listeners that
|
||||
are registered.
|
||||
|
||||
@see setCurrentRange
|
||||
*/
|
||||
void setCurrentRangeStart (double newStart,
|
||||
NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Returns the current thumb range.
|
||||
@see getCurrentRange, setCurrentRange
|
||||
*/
|
||||
Range<double> getCurrentRange() const noexcept { return visibleRange; }
|
||||
|
||||
/** Returns the position of the top of the thumb.
|
||||
@see getCurrentRange, setCurrentRangeStart
|
||||
*/
|
||||
double getCurrentRangeStart() const noexcept { return visibleRange.getStart(); }
|
||||
|
||||
/** Returns the current size of the thumb.
|
||||
@see getCurrentRange, setCurrentRange
|
||||
*/
|
||||
double getCurrentRangeSize() const noexcept { return visibleRange.getLength(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the amount by which the up and down buttons will move the bar.
|
||||
|
||||
The value here is in terms of the total range, and is added or subtracted
|
||||
from the thumb position when the user clicks an up/down (or left/right) button.
|
||||
*/
|
||||
void setSingleStepSize (double newSingleStepSize) noexcept;
|
||||
|
||||
/** Returns the current step size.
|
||||
@see setSingleStepSize
|
||||
*/
|
||||
double getSingleStepSize() const noexcept { return singleStepSize; }
|
||||
|
||||
/** Moves the scrollbar by a number of single-steps.
|
||||
|
||||
This will move the bar by a multiple of its single-step interval (as
|
||||
specified using the setSingleStepSize() method).
|
||||
|
||||
A positive value here will move the bar down or to the right, a negative
|
||||
value moves it up or to the left.
|
||||
|
||||
@param howManySteps the number of steps to move the scrollbar
|
||||
@param notification whether to send a notification of the change to listeners.
|
||||
A notification will only be sent if the position has changed.
|
||||
|
||||
@returns true if the scrollbar's position actually changed.
|
||||
*/
|
||||
bool moveScrollbarInSteps (int howManySteps,
|
||||
NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Moves the scroll bar up or down in pages.
|
||||
|
||||
This will move the bar by a multiple of its current thumb size, effectively
|
||||
doing a page-up or down.
|
||||
|
||||
A positive value here will move the bar down or to the right, a negative
|
||||
value moves it up or to the left.
|
||||
|
||||
@param howManyPages the number of pages to move the scrollbar
|
||||
@param notification whether to send a notification of the change to listeners.
|
||||
A notification will only be sent if the position has changed.
|
||||
|
||||
@returns true if the scrollbar's position actually changed.
|
||||
*/
|
||||
bool moveScrollbarInPages (int howManyPages,
|
||||
NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Scrolls to the top (or left).
|
||||
This is the same as calling setCurrentRangeStart (getMinimumRangeLimit());
|
||||
|
||||
@param notification whether to send a notification of the change to listeners.
|
||||
A notification will only be sent if the position has changed.
|
||||
|
||||
@returns true if the scrollbar's position actually changed.
|
||||
*/
|
||||
bool scrollToTop (NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Scrolls to the bottom (or right).
|
||||
This is the same as calling setCurrentRangeStart (getMaximumRangeLimit() - getCurrentRangeSize());
|
||||
|
||||
@param notification whether to send a notification of the change to listeners.
|
||||
A notification will only be sent if the position has changed.
|
||||
|
||||
@returns true if the scrollbar's position actually changed.
|
||||
*/
|
||||
bool scrollToBottom (NotificationType notification = sendNotificationAsync);
|
||||
|
||||
/** Changes the delay before the up and down buttons autorepeat when they are held
|
||||
down.
|
||||
|
||||
For an explanation of what the parameters are for, see Button::setRepeatSpeed().
|
||||
|
||||
@see Button::setRepeatSpeed
|
||||
*/
|
||||
void setButtonRepeatSpeed (int initialDelayInMillisecs,
|
||||
int repeatDelayInMillisecs,
|
||||
int minimumDelayInMillisecs = -1);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the component.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1000300, /**< The background colour of the scrollbar. */
|
||||
thumbColourId = 0x1000400, /**< A base colour to use for the thumb. The look and feel will probably use variations on this colour. */
|
||||
trackColourId = 0x1000401 /**< A base colour to use for the slot area of the bar. The look and feel will probably use variations on this colour. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for receiving events from a ScrollBar.
|
||||
|
||||
You can register a ScrollBar::Listener with a ScrollBar using the ScrollBar::addListener()
|
||||
method, and it will be called when the bar's position changes.
|
||||
|
||||
@see ScrollBar::addListener, ScrollBar::removeListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called when a ScrollBar is moved.
|
||||
|
||||
@param scrollBarThatHasMoved the bar that has moved
|
||||
@param newRangeStart the new range start of this bar
|
||||
*/
|
||||
virtual void scrollBarMoved (ScrollBar* scrollBarThatHasMoved,
|
||||
double newRangeStart) = 0;
|
||||
};
|
||||
|
||||
/** Registers a listener that will be called when the scrollbar is moved. */
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Deregisters a previously-registered listener. */
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
scrollbar-drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual bool areScrollbarButtonsVisible() = 0;
|
||||
|
||||
/** Draws one of the buttons on a scrollbar.
|
||||
|
||||
@param g the context to draw into
|
||||
@param scrollbar the bar itself
|
||||
@param width the width of the button
|
||||
@param height the height of the button
|
||||
@param buttonDirection the direction of the button, where 0 = up, 1 = right, 2 = down, 3 = left
|
||||
@param isScrollbarVertical true if it's a vertical bar, false if horizontal
|
||||
@param isMouseOverButton whether the mouse is currently over the button (also true if it's held down)
|
||||
@param isButtonDown whether the mouse button's held down
|
||||
*/
|
||||
virtual void drawScrollbarButton (Graphics& g,
|
||||
ScrollBar& scrollbar,
|
||||
int width, int height,
|
||||
int buttonDirection,
|
||||
bool isScrollbarVertical,
|
||||
bool isMouseOverButton,
|
||||
bool isButtonDown) = 0;
|
||||
|
||||
/** Draws the thumb area of a scrollbar.
|
||||
|
||||
@param g the context to draw into
|
||||
@param scrollbar the bar itself
|
||||
@param x the x position of the left edge of the thumb area to draw in
|
||||
@param y the y position of the top edge of the thumb area to draw in
|
||||
@param width the width of the thumb area to draw in
|
||||
@param height the height of the thumb area to draw in
|
||||
@param isScrollbarVertical true if it's a vertical bar, false if horizontal
|
||||
@param thumbStartPosition for vertical bars, the y coordinate of the top of the
|
||||
thumb, or its x position for horizontal bars
|
||||
@param thumbSize for vertical bars, the height of the thumb, or its width for
|
||||
horizontal bars. This may be 0 if the thumb shouldn't be drawn.
|
||||
@param isMouseOver whether the mouse is over the thumb area, also true if the mouse is
|
||||
currently dragging the thumb
|
||||
@param isMouseDown whether the mouse is currently dragging the scrollbar
|
||||
*/
|
||||
virtual void drawScrollbar (Graphics& g, ScrollBar& scrollbar,
|
||||
int x, int y, int width, int height,
|
||||
bool isScrollbarVertical,
|
||||
int thumbStartPosition,
|
||||
int thumbSize,
|
||||
bool isMouseOver,
|
||||
bool isMouseDown) = 0;
|
||||
|
||||
/** Returns the component effect to use for a scrollbar */
|
||||
virtual ImageEffectFilter* getScrollbarEffect() = 0;
|
||||
|
||||
/** Returns the minimum length in pixels to use for a scrollbar thumb. */
|
||||
virtual int getMinimumScrollbarThumbSize (ScrollBar&) = 0;
|
||||
|
||||
/** Returns the default thickness to use for a scrollbar. */
|
||||
virtual int getDefaultScrollbarWidth() = 0;
|
||||
|
||||
/** Returns the length in pixels to use for a scrollbar button. */
|
||||
virtual int getScrollbarButtonSize (ScrollBar&) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void setVisible (bool) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Range<double> totalRange { 0.0, 1.0 }, visibleRange { 0.0, 1.0 };
|
||||
double singleStepSize = 0.1, dragStartRange = 0;
|
||||
int thumbAreaStart = 0, thumbAreaSize = 0, thumbStart = 0, thumbSize = 0;
|
||||
int dragStartMousePos = 0, lastMousePos = 0;
|
||||
int initialDelayInMillisecs = 100, repeatDelayInMillisecs = 50, minimumDelayInMillisecs = 10;
|
||||
bool vertical, isDraggingThumb = false, autohides = true, userVisibilityFlag = false;
|
||||
class ScrollbarButton;
|
||||
std::unique_ptr<ScrollbarButton> upButton, downButton;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void handleAsyncUpdate() override;
|
||||
void updateThumbPosition();
|
||||
void timerCallback() override;
|
||||
bool getVisibility() const noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScrollBar)
|
||||
};
|
||||
|
||||
|
||||
} // namespace juce
|
303
deps/juce/modules/juce_gui_basics/layout/juce_SidePanel.cpp
vendored
Normal file
303
deps/juce/modules/juce_gui_basics/layout/juce_SidePanel.cpp
vendored
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
SidePanel::SidePanel (StringRef title, int width, bool positionOnLeft,
|
||||
Component* contentToDisplay, bool deleteComponentWhenNoLongerNeeded)
|
||||
: titleLabel ("titleLabel", title),
|
||||
isOnLeft (positionOnLeft),
|
||||
panelWidth (width)
|
||||
{
|
||||
lookAndFeelChanged();
|
||||
|
||||
addAndMakeVisible (titleLabel);
|
||||
|
||||
dismissButton.onClick = [this] { showOrHide (false); };
|
||||
addAndMakeVisible (dismissButton);
|
||||
|
||||
auto& desktop = Desktop::getInstance();
|
||||
|
||||
desktop.addGlobalMouseListener (this);
|
||||
desktop.getAnimator().addChangeListener (this);
|
||||
|
||||
if (contentToDisplay != nullptr)
|
||||
setContent (contentToDisplay, deleteComponentWhenNoLongerNeeded);
|
||||
|
||||
setOpaque (false);
|
||||
setVisible (false);
|
||||
setAlwaysOnTop (true);
|
||||
}
|
||||
|
||||
SidePanel::~SidePanel()
|
||||
{
|
||||
auto& desktop = Desktop::getInstance();
|
||||
|
||||
desktop.removeGlobalMouseListener (this);
|
||||
desktop.getAnimator().removeChangeListener (this);
|
||||
|
||||
if (parent != nullptr)
|
||||
parent->removeComponentListener (this);
|
||||
}
|
||||
|
||||
void SidePanel::setContent (Component* newContent, bool deleteComponentWhenNoLongerNeeded)
|
||||
{
|
||||
if (contentComponent.get() != newContent)
|
||||
{
|
||||
if (deleteComponentWhenNoLongerNeeded)
|
||||
contentComponent.setOwned (newContent);
|
||||
else
|
||||
contentComponent.setNonOwned (newContent);
|
||||
|
||||
addAndMakeVisible (contentComponent);
|
||||
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
void SidePanel::setTitleBarComponent (Component* titleBarComponentToUse,
|
||||
bool keepDismissButton,
|
||||
bool deleteComponentWhenNoLongerNeeded)
|
||||
{
|
||||
if (titleBarComponent.get() != titleBarComponentToUse)
|
||||
{
|
||||
if (deleteComponentWhenNoLongerNeeded)
|
||||
titleBarComponent.setOwned (titleBarComponentToUse);
|
||||
else
|
||||
titleBarComponent.setNonOwned (titleBarComponentToUse);
|
||||
|
||||
addAndMakeVisible (titleBarComponent);
|
||||
|
||||
resized();
|
||||
}
|
||||
|
||||
shouldShowDismissButton = keepDismissButton;
|
||||
}
|
||||
|
||||
void SidePanel::showOrHide (bool show)
|
||||
{
|
||||
if (parent != nullptr)
|
||||
{
|
||||
isShowing = show;
|
||||
|
||||
Desktop::getInstance().getAnimator().animateComponent (this, calculateBoundsInParent (*parent),
|
||||
1.0f, 250, true, 1.0, 0.0);
|
||||
|
||||
if (isShowing && ! isVisible())
|
||||
setVisible (true);
|
||||
}
|
||||
}
|
||||
|
||||
void SidePanel::moved()
|
||||
{
|
||||
if (onPanelMove != nullptr)
|
||||
onPanelMove();
|
||||
}
|
||||
|
||||
void SidePanel::resized()
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
calculateAndRemoveShadowBounds (bounds);
|
||||
|
||||
auto titleBounds = bounds.removeFromTop (titleBarHeight);
|
||||
|
||||
if (titleBarComponent != nullptr)
|
||||
{
|
||||
if (shouldShowDismissButton)
|
||||
dismissButton.setBounds (isOnLeft ? titleBounds.removeFromRight (30).withTrimmedRight (10)
|
||||
: titleBounds.removeFromLeft (30).withTrimmedLeft (10));
|
||||
|
||||
titleBarComponent->setBounds (titleBounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
dismissButton.setBounds (isOnLeft ? titleBounds.removeFromRight (30).withTrimmedRight (10)
|
||||
: titleBounds.removeFromLeft (30).withTrimmedLeft (10));
|
||||
|
||||
titleLabel.setBounds (isOnLeft ? titleBounds.withTrimmedRight (40)
|
||||
: titleBounds.withTrimmedLeft (40));
|
||||
}
|
||||
|
||||
if (contentComponent != nullptr)
|
||||
contentComponent->setBounds (bounds);
|
||||
}
|
||||
|
||||
void SidePanel::paint (Graphics& g)
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
auto bgColour = lf.findColour (SidePanel::backgroundColour);
|
||||
auto shadowColour = lf.findColour (SidePanel::shadowBaseColour);
|
||||
|
||||
g.setGradientFill (ColourGradient (shadowColour.withAlpha (0.7f), (isOnLeft ? shadowArea.getTopLeft()
|
||||
: shadowArea.getTopRight()).toFloat(),
|
||||
shadowColour.withAlpha (0.0f), (isOnLeft ? shadowArea.getTopRight()
|
||||
: shadowArea.getTopLeft()).toFloat(), false));
|
||||
g.fillRect (shadowArea);
|
||||
|
||||
g.excludeClipRegion (shadowArea);
|
||||
g.fillAll (bgColour);
|
||||
}
|
||||
|
||||
void SidePanel::parentHierarchyChanged()
|
||||
{
|
||||
auto* newParent = getParentComponent();
|
||||
|
||||
if ((newParent != nullptr) && (parent != newParent))
|
||||
{
|
||||
if (parent != nullptr)
|
||||
parent->removeComponentListener (this);
|
||||
|
||||
parent = newParent;
|
||||
parent->addComponentListener (this);
|
||||
}
|
||||
}
|
||||
|
||||
void SidePanel::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
if (shouldResize)
|
||||
{
|
||||
Point<int> convertedPoint;
|
||||
|
||||
if (getParentComponent() == nullptr)
|
||||
convertedPoint = e.eventComponent->localPointToGlobal (e.getPosition());
|
||||
else
|
||||
convertedPoint = getParentComponent()->getLocalPoint (e.eventComponent, e.getPosition());
|
||||
|
||||
auto currentMouseDragX = convertedPoint.x;
|
||||
|
||||
if (isOnLeft)
|
||||
{
|
||||
amountMoved = startingBounds.getRight() - currentMouseDragX;
|
||||
setBounds (getBounds().withX (startingBounds.getX() - jmax (amountMoved, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
amountMoved = currentMouseDragX - startingBounds.getX();
|
||||
setBounds (getBounds().withX (startingBounds.getX() + jmax (amountMoved, 0)));
|
||||
}
|
||||
}
|
||||
else if (isShowing)
|
||||
{
|
||||
auto relativeMouseDownPosition = getLocalPoint (e.eventComponent, e.getMouseDownPosition());
|
||||
auto relativeMouseDragPosition = getLocalPoint (e.eventComponent, e.getPosition());
|
||||
|
||||
if (! getLocalBounds().contains (relativeMouseDownPosition)
|
||||
&& getLocalBounds().contains (relativeMouseDragPosition))
|
||||
{
|
||||
shouldResize = true;
|
||||
startingBounds = getBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SidePanel::mouseUp (const MouseEvent&)
|
||||
{
|
||||
if (shouldResize)
|
||||
{
|
||||
showOrHide (amountMoved < (panelWidth / 2));
|
||||
|
||||
amountMoved = 0;
|
||||
shouldResize = false;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void SidePanel::lookAndFeelChanged()
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
dismissButton.setShape (lf.getSidePanelDismissButtonShape (*this), false, true, false);
|
||||
|
||||
dismissButton.setColours (lf.findColour (SidePanel::dismissButtonNormalColour),
|
||||
lf.findColour (SidePanel::dismissButtonOverColour),
|
||||
lf.findColour (SidePanel::dismissButtonDownColour));
|
||||
|
||||
titleLabel.setFont (lf.getSidePanelTitleFont (*this));
|
||||
titleLabel.setColour (Label::textColourId, findColour (SidePanel::titleTextColour));
|
||||
titleLabel.setJustificationType (lf.getSidePanelTitleJustification (*this));
|
||||
}
|
||||
|
||||
void SidePanel::componentMovedOrResized (Component& component, bool wasMoved, bool wasResized)
|
||||
{
|
||||
ignoreUnused (wasMoved);
|
||||
|
||||
if (wasResized && (&component == parent))
|
||||
setBounds (calculateBoundsInParent (component));
|
||||
}
|
||||
|
||||
void SidePanel::changeListenerCallback (ChangeBroadcaster*)
|
||||
{
|
||||
if (! Desktop::getInstance().getAnimator().isAnimating (this))
|
||||
{
|
||||
if (onPanelShowHide != nullptr)
|
||||
onPanelShowHide (isShowing);
|
||||
|
||||
if (isVisible() && ! isShowing)
|
||||
setVisible (false);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle<int> SidePanel::calculateBoundsInParent (Component& parentComp) const
|
||||
{
|
||||
auto parentBounds = parentComp.getLocalBounds();
|
||||
|
||||
if (isOnLeft)
|
||||
{
|
||||
return isShowing ? parentBounds.removeFromLeft (panelWidth)
|
||||
: parentBounds.withX (parentBounds.getX() - panelWidth).withWidth (panelWidth);
|
||||
}
|
||||
|
||||
return isShowing ? parentBounds.removeFromRight (panelWidth)
|
||||
: parentBounds.withX (parentBounds.getRight()).withWidth (panelWidth);
|
||||
}
|
||||
|
||||
void SidePanel::calculateAndRemoveShadowBounds (Rectangle<int>& bounds)
|
||||
{
|
||||
shadowArea = isOnLeft ? bounds.removeFromRight (shadowWidth)
|
||||
: bounds.removeFromLeft (shadowWidth);
|
||||
}
|
||||
|
||||
bool SidePanel::isMouseEventInThisOrChildren (Component* eventComponent)
|
||||
{
|
||||
if (eventComponent == this)
|
||||
return true;
|
||||
|
||||
for (auto& child : getChildren())
|
||||
if (eventComponent == child)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> SidePanel::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
|
||||
}
|
||||
|
||||
} // namespace juce
|
238
deps/juce/modules/juce_gui_basics/layout/juce_SidePanel.h
vendored
Normal file
238
deps/juce/modules/juce_gui_basics/layout/juce_SidePanel.h
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that is positioned on either the left- or right-hand side of its parent,
|
||||
containing a header and some content. This sort of component is typically used for
|
||||
navigation and forms in mobile applications.
|
||||
|
||||
When triggered with the showOrHide() method, the SidePanel will animate itself to its
|
||||
new position. This component also contains some logic to reactively resize and dismiss
|
||||
itself when the user drags it.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class SidePanel : public Component,
|
||||
private ComponentListener,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a SidePanel component.
|
||||
|
||||
@param title the text to use for the SidePanel's title bar
|
||||
@param width the width of the SidePanel
|
||||
@param positionOnLeft if true, the SidePanel will be positioned on the left of its parent component and
|
||||
if false, the SidePanel will be positioned on the right of its parent component
|
||||
@param contentComponent the component to add to this SidePanel - this content will take up the full
|
||||
size of the SidePanel, minus the height of the title bar. You can pass nullptr
|
||||
to this if you like and set the content component later using the setContent() method
|
||||
@param deleteComponentWhenNoLongerNeeded if true, the component will be deleted automatically when
|
||||
the SidePanel is deleted or when a different component is added. If false,
|
||||
the caller must manage the lifetime of the component
|
||||
*/
|
||||
SidePanel (StringRef title, int width, bool positionOnLeft,
|
||||
Component* contentComponent = nullptr,
|
||||
bool deleteComponentWhenNoLongerNeeded = true);
|
||||
|
||||
/** Destructor */
|
||||
~SidePanel() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the component that this SidePanel will contain.
|
||||
|
||||
This will add the given component to this SidePanel and position it below the title bar.
|
||||
|
||||
(Don't add or remove any child components directly using the normal
|
||||
Component::addChildComponent() methods).
|
||||
|
||||
@param newContentComponent the component to add to this SidePanel, or nullptr to remove
|
||||
the current component.
|
||||
@param deleteComponentWhenNoLongerNeeded if true, the component will be deleted automatically when
|
||||
the SidePanel is deleted or when a different component is added. If false,
|
||||
the caller must manage the lifetime of the component
|
||||
|
||||
@see getContent
|
||||
*/
|
||||
void setContent (Component* newContentComponent,
|
||||
bool deleteComponentWhenNoLongerNeeded = true);
|
||||
|
||||
/** Returns the component that's currently being used inside the SidePanel.
|
||||
|
||||
@see setViewedComponent
|
||||
*/
|
||||
Component* getContent() const noexcept { return contentComponent.get(); }
|
||||
|
||||
/** Sets a custom component to be used for the title bar of this SidePanel, replacing
|
||||
the default. You can pass a nullptr to revert to the default title bar.
|
||||
|
||||
@param titleBarComponentToUse the component to use as the title bar, or nullptr to use
|
||||
the default
|
||||
@param keepDismissButton if false the specified component will take up the full width of
|
||||
the title bar including the dismiss button but if true, the default
|
||||
dismiss button will be kept
|
||||
@param deleteComponentWhenNoLongerNeeded if true, the component will be deleted automatically when
|
||||
the SidePanel is deleted or when a different component is added. If false,
|
||||
the caller must manage the lifetime of the component
|
||||
|
||||
@see getTitleBarComponent
|
||||
*/
|
||||
void setTitleBarComponent (Component* titleBarComponentToUse,
|
||||
bool keepDismissButton,
|
||||
bool deleteComponentWhenNoLongerNeeded = true);
|
||||
|
||||
/** Returns the component that is currently being used as the title bar of the SidePanel.
|
||||
|
||||
@see setTitleBarComponent
|
||||
*/
|
||||
Component* getTitleBarComponent() const noexcept { return titleBarComponent.get(); }
|
||||
|
||||
/** Shows or hides the SidePanel.
|
||||
|
||||
This will animate the SidePanel to either its full width or to be hidden on the
|
||||
left- or right-hand side of its parent component depending on the value of positionOnLeft
|
||||
that was passed to the constructor.
|
||||
|
||||
@param show if true, this will show the SidePanel and if false the SidePanel will be hidden
|
||||
*/
|
||||
void showOrHide (bool show);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the SidePanel is currently showing. */
|
||||
bool isPanelShowing() const noexcept { return isShowing; }
|
||||
|
||||
/** Returns true if the SidePanel is positioned on the left of its parent. */
|
||||
bool isPanelOnLeft() const noexcept { return isOnLeft; }
|
||||
|
||||
/** Sets the width of the shadow that will be drawn on the side of the panel. */
|
||||
void setShadowWidth (int newWidth) noexcept { shadowWidth = newWidth; }
|
||||
|
||||
/** Returns the width of the shadow that will be drawn on the side of the panel. */
|
||||
int getShadowWidth() const noexcept { return shadowWidth; }
|
||||
|
||||
/** Sets the height of the title bar at the top of the SidePanel. */
|
||||
void setTitleBarHeight (int newHeight) noexcept { titleBarHeight = newHeight; }
|
||||
|
||||
/** Returns the height of the title bar at the top of the SidePanel. */
|
||||
int getTitleBarHeight() const noexcept { return titleBarHeight; }
|
||||
|
||||
/** Returns the text that is displayed in the title bar at the top of the SidePanel. */
|
||||
String getTitleText() const noexcept { return titleLabel.getText(); }
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
SidePanel drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual Font getSidePanelTitleFont (SidePanel&) = 0;
|
||||
virtual Justification getSidePanelTitleJustification (SidePanel&) = 0;
|
||||
virtual Path getSidePanelDismissButtonShape (SidePanel&) = 0;
|
||||
};
|
||||
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the SidePanel.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColour = 0x100f001,
|
||||
titleTextColour = 0x100f002,
|
||||
shadowBaseColour = 0x100f003,
|
||||
dismissButtonNormalColour = 0x100f004,
|
||||
dismissButtonOverColour = 0x100f005,
|
||||
dismissButtonDownColour = 0x100f006
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** You can assign a lambda to this callback object and it will be called when the panel is moved. */
|
||||
std::function<void()> onPanelMove;
|
||||
|
||||
/** You can assign a lambda to this callback object and it will be called when the panel is shown or hidden. */
|
||||
std::function<void (bool)> onPanelShowHide;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void moved() override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void paint (Graphics& g) override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Component* parent = nullptr;
|
||||
OptionalScopedPointer<Component> contentComponent;
|
||||
OptionalScopedPointer<Component> titleBarComponent;
|
||||
|
||||
Label titleLabel;
|
||||
ShapeButton dismissButton { "dismissButton", Colours::lightgrey, Colours::lightgrey, Colours::white };
|
||||
|
||||
Rectangle<int> shadowArea;
|
||||
|
||||
bool isOnLeft = false;
|
||||
bool isShowing = false;
|
||||
|
||||
int panelWidth = 0;
|
||||
int shadowWidth = 15;
|
||||
int titleBarHeight = 40;
|
||||
|
||||
Rectangle<int> startingBounds;
|
||||
bool shouldResize = false;
|
||||
int amountMoved = 0;
|
||||
|
||||
bool shouldShowDismissButton = true;
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void lookAndFeelChanged() override;
|
||||
void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
|
||||
void changeListenerCallback (ChangeBroadcaster*) override;
|
||||
|
||||
Rectangle<int> calculateBoundsInParent (Component&) const;
|
||||
void calculateAndRemoveShadowBounds (Rectangle<int>& bounds);
|
||||
|
||||
bool isMouseEventInThisOrChildren (Component*);
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SidePanel)
|
||||
};
|
||||
|
||||
} // namespace juce
|
342
deps/juce/modules/juce_gui_basics/layout/juce_StretchableLayoutManager.cpp
vendored
Normal file
342
deps/juce/modules/juce_gui_basics/layout/juce_StretchableLayoutManager.cpp
vendored
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
StretchableLayoutManager::StretchableLayoutManager() {}
|
||||
StretchableLayoutManager::~StretchableLayoutManager() {}
|
||||
|
||||
//==============================================================================
|
||||
void StretchableLayoutManager::clearAllItems()
|
||||
{
|
||||
items.clear();
|
||||
totalSize = 0;
|
||||
}
|
||||
|
||||
void StretchableLayoutManager::setItemLayout (const int itemIndex,
|
||||
const double minimumSize,
|
||||
const double maximumSize,
|
||||
const double preferredSize)
|
||||
{
|
||||
auto* layout = getInfoFor (itemIndex);
|
||||
|
||||
if (layout == nullptr)
|
||||
{
|
||||
layout = new ItemLayoutProperties();
|
||||
layout->itemIndex = itemIndex;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < items.size(); ++i)
|
||||
if (items.getUnchecked (i)->itemIndex > itemIndex)
|
||||
break;
|
||||
|
||||
items.insert (i, layout);
|
||||
}
|
||||
|
||||
layout->minSize = minimumSize;
|
||||
layout->maxSize = maximumSize;
|
||||
layout->preferredSize = preferredSize;
|
||||
layout->currentSize = 0;
|
||||
}
|
||||
|
||||
bool StretchableLayoutManager::getItemLayout (const int itemIndex,
|
||||
double& minimumSize,
|
||||
double& maximumSize,
|
||||
double& preferredSize) const
|
||||
{
|
||||
if (auto* layout = getInfoFor (itemIndex))
|
||||
{
|
||||
minimumSize = layout->minSize;
|
||||
maximumSize = layout->maxSize;
|
||||
preferredSize = layout->preferredSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void StretchableLayoutManager::setTotalSize (const int newTotalSize)
|
||||
{
|
||||
totalSize = newTotalSize;
|
||||
|
||||
fitComponentsIntoSpace (0, items.size(), totalSize, 0);
|
||||
}
|
||||
|
||||
int StretchableLayoutManager::getItemCurrentPosition (const int itemIndex) const
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
for (int i = 0; i < itemIndex; ++i)
|
||||
if (auto* layout = getInfoFor (i))
|
||||
pos += layout->currentSize;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
int StretchableLayoutManager::getItemCurrentAbsoluteSize (const int itemIndex) const
|
||||
{
|
||||
if (auto* layout = getInfoFor (itemIndex))
|
||||
return layout->currentSize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double StretchableLayoutManager::getItemCurrentRelativeSize (const int itemIndex) const
|
||||
{
|
||||
if (auto* layout = getInfoFor (itemIndex))
|
||||
return -layout->currentSize / (double) totalSize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StretchableLayoutManager::setItemPosition (const int itemIndex,
|
||||
int newPosition)
|
||||
{
|
||||
for (int i = items.size(); --i >= 0;)
|
||||
{
|
||||
auto* layout = items.getUnchecked(i);
|
||||
|
||||
if (layout->itemIndex == itemIndex)
|
||||
{
|
||||
auto realTotalSize = jmax (totalSize, getMinimumSizeOfItems (0, items.size()));
|
||||
auto minSizeAfterThisComp = getMinimumSizeOfItems (i, items.size());
|
||||
auto maxSizeAfterThisComp = getMaximumSizeOfItems (i + 1, items.size());
|
||||
|
||||
newPosition = jmax (newPosition, totalSize - maxSizeAfterThisComp - layout->currentSize);
|
||||
newPosition = jmin (newPosition, realTotalSize - minSizeAfterThisComp);
|
||||
|
||||
auto endPos = fitComponentsIntoSpace (0, i, newPosition, 0);
|
||||
|
||||
endPos += layout->currentSize;
|
||||
|
||||
fitComponentsIntoSpace (i + 1, items.size(), totalSize - endPos, endPos);
|
||||
updatePrefSizesToMatchCurrentPositions();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void StretchableLayoutManager::layOutComponents (Component** const components,
|
||||
int numComponents,
|
||||
int x, int y, int w, int h,
|
||||
const bool vertically,
|
||||
const bool resizeOtherDimension)
|
||||
{
|
||||
setTotalSize (vertically ? h : w);
|
||||
int pos = vertically ? y : x;
|
||||
|
||||
for (int i = 0; i < numComponents; ++i)
|
||||
{
|
||||
if (auto* layout = getInfoFor (i))
|
||||
{
|
||||
if (auto* c = components[i])
|
||||
{
|
||||
if (i == numComponents - 1)
|
||||
{
|
||||
// if it's the last item, crop it to exactly fit the available space..
|
||||
if (resizeOtherDimension)
|
||||
{
|
||||
if (vertically)
|
||||
c->setBounds (x, pos, w, jmax (layout->currentSize, h - pos));
|
||||
else
|
||||
c->setBounds (pos, y, jmax (layout->currentSize, w - pos), h);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vertically)
|
||||
c->setBounds (c->getX(), pos, c->getWidth(), jmax (layout->currentSize, h - pos));
|
||||
else
|
||||
c->setBounds (pos, c->getY(), jmax (layout->currentSize, w - pos), c->getHeight());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (resizeOtherDimension)
|
||||
{
|
||||
if (vertically)
|
||||
c->setBounds (x, pos, w, layout->currentSize);
|
||||
else
|
||||
c->setBounds (pos, y, layout->currentSize, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vertically)
|
||||
c->setBounds (c->getX(), pos, c->getWidth(), layout->currentSize);
|
||||
else
|
||||
c->setBounds (pos, c->getY(), layout->currentSize, c->getHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pos += layout->currentSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
StretchableLayoutManager::ItemLayoutProperties* StretchableLayoutManager::getInfoFor (const int itemIndex) const
|
||||
{
|
||||
for (auto* i : items)
|
||||
if (i->itemIndex == itemIndex)
|
||||
return i;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int StretchableLayoutManager::fitComponentsIntoSpace (const int startIndex,
|
||||
const int endIndex,
|
||||
const int availableSpace,
|
||||
int startPos)
|
||||
{
|
||||
// calculate the total sizes
|
||||
double totalIdealSize = 0.0;
|
||||
int totalMinimums = 0;
|
||||
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
{
|
||||
auto* layout = items.getUnchecked (i);
|
||||
|
||||
layout->currentSize = sizeToRealSize (layout->minSize, totalSize);
|
||||
|
||||
totalMinimums += layout->currentSize;
|
||||
totalIdealSize += sizeToRealSize (layout->preferredSize, totalSize);
|
||||
}
|
||||
|
||||
if (totalIdealSize <= 0)
|
||||
totalIdealSize = 1.0;
|
||||
|
||||
// now calc the best sizes..
|
||||
int extraSpace = availableSpace - totalMinimums;
|
||||
|
||||
while (extraSpace > 0)
|
||||
{
|
||||
int numWantingMoreSpace = 0;
|
||||
int numHavingTakenExtraSpace = 0;
|
||||
|
||||
// first figure out how many comps want a slice of the extra space..
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
{
|
||||
auto* layout = items.getUnchecked (i);
|
||||
|
||||
auto sizeWanted = sizeToRealSize (layout->preferredSize, totalSize);
|
||||
|
||||
auto bestSize = jlimit (layout->currentSize,
|
||||
jmax (layout->currentSize,
|
||||
sizeToRealSize (layout->maxSize, totalSize)),
|
||||
roundToInt (sizeWanted * availableSpace / totalIdealSize));
|
||||
|
||||
if (bestSize > layout->currentSize)
|
||||
++numWantingMoreSpace;
|
||||
}
|
||||
|
||||
// ..share out the extra space..
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
{
|
||||
auto* layout = items.getUnchecked (i);
|
||||
|
||||
auto sizeWanted = sizeToRealSize (layout->preferredSize, totalSize);
|
||||
|
||||
auto bestSize = jlimit (layout->currentSize,
|
||||
jmax (layout->currentSize, sizeToRealSize (layout->maxSize, totalSize)),
|
||||
roundToInt (sizeWanted * availableSpace / totalIdealSize));
|
||||
|
||||
auto extraWanted = bestSize - layout->currentSize;
|
||||
|
||||
if (extraWanted > 0)
|
||||
{
|
||||
auto extraAllowed = jmin (extraWanted,
|
||||
extraSpace / jmax (1, numWantingMoreSpace));
|
||||
|
||||
if (extraAllowed > 0)
|
||||
{
|
||||
++numHavingTakenExtraSpace;
|
||||
--numWantingMoreSpace;
|
||||
|
||||
layout->currentSize += extraAllowed;
|
||||
extraSpace -= extraAllowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numHavingTakenExtraSpace <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// ..and calculate the end position
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
{
|
||||
auto* layout = items.getUnchecked(i);
|
||||
startPos += layout->currentSize;
|
||||
}
|
||||
|
||||
return startPos;
|
||||
}
|
||||
|
||||
int StretchableLayoutManager::getMinimumSizeOfItems (const int startIndex,
|
||||
const int endIndex) const
|
||||
{
|
||||
int totalMinimums = 0;
|
||||
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
totalMinimums += sizeToRealSize (items.getUnchecked (i)->minSize, totalSize);
|
||||
|
||||
return totalMinimums;
|
||||
}
|
||||
|
||||
int StretchableLayoutManager::getMaximumSizeOfItems (const int startIndex, const int endIndex) const
|
||||
{
|
||||
int totalMaximums = 0;
|
||||
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
totalMaximums += sizeToRealSize (items.getUnchecked (i)->maxSize, totalSize);
|
||||
|
||||
return totalMaximums;
|
||||
}
|
||||
|
||||
void StretchableLayoutManager::updatePrefSizesToMatchCurrentPositions()
|
||||
{
|
||||
for (int i = 0; i < items.size(); ++i)
|
||||
{
|
||||
auto* layout = items.getUnchecked (i);
|
||||
|
||||
layout->preferredSize
|
||||
= (layout->preferredSize < 0) ? getItemCurrentRelativeSize (i)
|
||||
: getItemCurrentAbsoluteSize (i);
|
||||
}
|
||||
}
|
||||
|
||||
int StretchableLayoutManager::sizeToRealSize (double size, int totalSpace)
|
||||
{
|
||||
if (size < 0)
|
||||
size *= -totalSpace;
|
||||
|
||||
return roundToInt (size);
|
||||
}
|
||||
|
||||
} // namespace juce
|
261
deps/juce/modules/juce_gui_basics/layout/juce_StretchableLayoutManager.h
vendored
Normal file
261
deps/juce/modules/juce_gui_basics/layout/juce_StretchableLayoutManager.h
vendored
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
For laying out a set of components, where the components have preferred sizes
|
||||
and size limits, but where they are allowed to stretch to fill the available
|
||||
space.
|
||||
|
||||
For example, if you have a component containing several other components, and
|
||||
each one should be given a share of the total size, you could use one of these
|
||||
to resize the child components when the parent component is resized. Then
|
||||
you could add a StretchableLayoutResizerBar to easily let the user rescale them.
|
||||
|
||||
A StretchableLayoutManager operates only in one dimension, so if you have a set
|
||||
of components stacked vertically on top of each other, you'd use one to manage their
|
||||
heights. To build up complex arrangements of components, e.g. for applications
|
||||
with multiple nested panels, you would use more than one StretchableLayoutManager.
|
||||
E.g. by using two (one vertical, one horizontal), you could create a resizable
|
||||
spreadsheet-style table.
|
||||
|
||||
E.g.
|
||||
@code
|
||||
class MyComp : public Component
|
||||
{
|
||||
StretchableLayoutManager myLayout;
|
||||
|
||||
MyComp()
|
||||
{
|
||||
myLayout.setItemLayout (0, // for item 0
|
||||
50, 100, // must be between 50 and 100 pixels in size
|
||||
-0.6); // and its preferred size is 60% of the total available space
|
||||
|
||||
myLayout.setItemLayout (1, // for item 1
|
||||
-0.2, -0.6, // size must be between 20% and 60% of the available space
|
||||
50); // and its preferred size is 50 pixels
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
// make a list of two of our child components that we want to reposition
|
||||
Component* comps[] = { myComp1, myComp2 };
|
||||
|
||||
// this will position the 2 components, one above the other, to fit
|
||||
// vertically into the rectangle provided.
|
||||
myLayout.layOutComponents (comps, 2,
|
||||
0, 0, getWidth(), getHeight(),
|
||||
true);
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
@see StretchableLayoutResizerBar
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API StretchableLayoutManager
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty layout.
|
||||
|
||||
You'll need to add some item properties to the layout before it can be used
|
||||
to resize things - see setItemLayout().
|
||||
*/
|
||||
StretchableLayoutManager();
|
||||
|
||||
/** Destructor. */
|
||||
~StretchableLayoutManager();
|
||||
|
||||
//==============================================================================
|
||||
/** For a numbered item, this sets its size limits and preferred size.
|
||||
|
||||
@param itemIndex the index of the item to change.
|
||||
@param minimumSize the minimum size that this item is allowed to be - a positive number
|
||||
indicates an absolute size in pixels. A negative number indicates a
|
||||
proportion of the available space (e.g -0.5 is 50%)
|
||||
@param maximumSize the maximum size that this item is allowed to be - a positive number
|
||||
indicates an absolute size in pixels. A negative number indicates a
|
||||
proportion of the available space
|
||||
@param preferredSize the size that this item would like to be, if there's enough room. A
|
||||
positive number indicates an absolute size in pixels. A negative number
|
||||
indicates a proportion of the available space
|
||||
@see getItemLayout
|
||||
*/
|
||||
void setItemLayout (int itemIndex,
|
||||
double minimumSize,
|
||||
double maximumSize,
|
||||
double preferredSize);
|
||||
|
||||
/** For a numbered item, this returns its size limits and preferred size.
|
||||
|
||||
@param itemIndex the index of the item.
|
||||
@param minimumSize the minimum size that this item is allowed to be - a positive number
|
||||
indicates an absolute size in pixels. A negative number indicates a
|
||||
proportion of the available space (e.g -0.5 is 50%)
|
||||
@param maximumSize the maximum size that this item is allowed to be - a positive number
|
||||
indicates an absolute size in pixels. A negative number indicates a
|
||||
proportion of the available space
|
||||
@param preferredSize the size that this item would like to be, if there's enough room. A
|
||||
positive number indicates an absolute size in pixels. A negative number
|
||||
indicates a proportion of the available space
|
||||
@returns false if the item's properties hadn't been set
|
||||
@see setItemLayout
|
||||
*/
|
||||
bool getItemLayout (int itemIndex,
|
||||
double& minimumSize,
|
||||
double& maximumSize,
|
||||
double& preferredSize) const;
|
||||
|
||||
/** Clears all the properties that have been set with setItemLayout() and resets
|
||||
this object to its initial state.
|
||||
*/
|
||||
void clearAllItems();
|
||||
|
||||
//==============================================================================
|
||||
/** Takes a set of components that correspond to the layout's items, and positions
|
||||
them to fill a space.
|
||||
|
||||
This will try to give each item its preferred size, whether that's a relative size
|
||||
or an absolute one.
|
||||
|
||||
@param components an array of components that correspond to each of the
|
||||
numbered items that the StretchableLayoutManager object
|
||||
has been told about with setItemLayout()
|
||||
@param numComponents the number of components in the array that is passed-in. This
|
||||
should be the same as the number of items this object has been
|
||||
told about.
|
||||
@param x the left of the rectangle in which the components should
|
||||
be laid out
|
||||
@param y the top of the rectangle in which the components should
|
||||
be laid out
|
||||
@param width the width of the rectangle in which the components should
|
||||
be laid out
|
||||
@param height the height of the rectangle in which the components should
|
||||
be laid out
|
||||
@param vertically if true, the components will be positioned in a vertical stack,
|
||||
so that they fill the height of the rectangle. If false, they
|
||||
will be placed side-by-side in a horizontal line, filling the
|
||||
available width
|
||||
@param resizeOtherDimension if true, this means that the components will have their
|
||||
other dimension resized to fit the space - i.e. if the 'vertically'
|
||||
parameter is true, their x-positions and widths are adjusted to fit
|
||||
the x and width parameters; if 'vertically' is false, their y-positions
|
||||
and heights are adjusted to fit the y and height parameters.
|
||||
*/
|
||||
void layOutComponents (Component** components,
|
||||
int numComponents,
|
||||
int x, int y, int width, int height,
|
||||
bool vertically,
|
||||
bool resizeOtherDimension);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current position of one of the items.
|
||||
|
||||
This is only a valid call after layOutComponents() has been called, as it
|
||||
returns the last position that this item was placed at. If the layout was
|
||||
vertical, the value returned will be the y position of the top of the item,
|
||||
relative to the top of the rectangle in which the items were placed (so for
|
||||
example, item 0 will always have position of 0, even in the rectangle passed
|
||||
in to layOutComponents() wasn't at y = 0). If the layout was done horizontally,
|
||||
the position returned is the item's left-hand position, again relative to the
|
||||
x position of the rectangle used.
|
||||
|
||||
@see getItemCurrentSize, setItemPosition
|
||||
*/
|
||||
int getItemCurrentPosition (int itemIndex) const;
|
||||
|
||||
/** Returns the current size of one of the items.
|
||||
|
||||
This is only meaningful after layOutComponents() has been called, as it
|
||||
returns the last size that this item was given. If the layout was done
|
||||
vertically, it'll return the item's height in pixels; if it was horizontal,
|
||||
it'll return its width.
|
||||
|
||||
@see getItemCurrentRelativeSize
|
||||
*/
|
||||
int getItemCurrentAbsoluteSize (int itemIndex) const;
|
||||
|
||||
/** Returns the current size of one of the items.
|
||||
|
||||
This is only meaningful after layOutComponents() has been called, as it
|
||||
returns the last size that this item was given. If the layout was done
|
||||
vertically, it'll return a negative value representing the item's height relative
|
||||
to the last size used for laying the components out; if the layout was done
|
||||
horizontally it'll be the proportion of its width.
|
||||
|
||||
@see getItemCurrentAbsoluteSize
|
||||
*/
|
||||
double getItemCurrentRelativeSize (int itemIndex) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Moves one of the items, shifting along any other items as necessary in
|
||||
order to get it to the desired position.
|
||||
|
||||
Calling this method will also update the preferred sizes of the items it
|
||||
shuffles along, so that they reflect their new positions.
|
||||
|
||||
(This is the method that a StretchableLayoutResizerBar uses to shift the items
|
||||
about when it's dragged).
|
||||
|
||||
@param itemIndex the item to move
|
||||
@param newPosition the absolute position that you'd like this item to move
|
||||
to. The item might not be able to always reach exactly this position,
|
||||
because other items may have minimum sizes that constrain how
|
||||
far it can go
|
||||
*/
|
||||
void setItemPosition (int itemIndex,
|
||||
int newPosition);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct ItemLayoutProperties
|
||||
{
|
||||
int itemIndex;
|
||||
int currentSize;
|
||||
double minSize, maxSize, preferredSize;
|
||||
};
|
||||
|
||||
OwnedArray<ItemLayoutProperties> items;
|
||||
int totalSize = 0;
|
||||
|
||||
//==============================================================================
|
||||
static int sizeToRealSize (double size, int totalSpace);
|
||||
ItemLayoutProperties* getInfoFor (int itemIndex) const;
|
||||
void setTotalSize (int newTotalSize);
|
||||
int fitComponentsIntoSpace (int startIndex, int endIndex, int availableSpace, int startPos);
|
||||
int getMinimumSizeOfItems (int startIndex, int endIndex) const;
|
||||
int getMaximumSizeOfItems (int startIndex, int endIndex) const;
|
||||
void updatePrefSizesToMatchCurrentPositions();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableLayoutManager)
|
||||
};
|
||||
|
||||
} // namespace juce
|
79
deps/juce/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.cpp
vendored
Normal file
79
deps/juce/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.cpp
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
StretchableLayoutResizerBar::StretchableLayoutResizerBar (StretchableLayoutManager* layout_,
|
||||
const int index,
|
||||
const bool vertical)
|
||||
: layout (layout_),
|
||||
itemIndex (index),
|
||||
isVertical (vertical)
|
||||
{
|
||||
setRepaintsOnMouseActivity (true);
|
||||
setMouseCursor (vertical ? MouseCursor::LeftRightResizeCursor
|
||||
: MouseCursor::UpDownResizeCursor);
|
||||
}
|
||||
|
||||
StretchableLayoutResizerBar::~StretchableLayoutResizerBar()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void StretchableLayoutResizerBar::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawStretchableLayoutResizerBar (g,
|
||||
getWidth(), getHeight(),
|
||||
isVertical,
|
||||
isMouseOver(),
|
||||
isMouseButtonDown());
|
||||
}
|
||||
|
||||
void StretchableLayoutResizerBar::mouseDown (const MouseEvent&)
|
||||
{
|
||||
mouseDownPos = layout->getItemCurrentPosition (itemIndex);
|
||||
}
|
||||
|
||||
void StretchableLayoutResizerBar::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
const int desiredPos = mouseDownPos + (isVertical ? e.getDistanceFromDragStartX()
|
||||
: e.getDistanceFromDragStartY());
|
||||
|
||||
|
||||
if (layout->getItemCurrentPosition (itemIndex) != desiredPos)
|
||||
{
|
||||
layout->setItemPosition (itemIndex, desiredPos);
|
||||
hasBeenMoved();
|
||||
}
|
||||
}
|
||||
|
||||
void StretchableLayoutResizerBar::hasBeenMoved()
|
||||
{
|
||||
if (Component* parent = getParentComponent())
|
||||
parent->resized();
|
||||
}
|
||||
|
||||
} // namespace juce
|
105
deps/juce/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.h
vendored
Normal file
105
deps/juce/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.h
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that acts as one of the vertical or horizontal bars you see being
|
||||
used to resize panels in a window.
|
||||
|
||||
One of these acts with a StretchableLayoutManager to resize the other components.
|
||||
|
||||
@see StretchableLayoutManager
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API StretchableLayoutResizerBar : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a resizer bar for use on a specified layout.
|
||||
|
||||
@param layoutToUse the layout that will be affected when this bar
|
||||
is dragged
|
||||
@param itemIndexInLayout the item index in the layout that corresponds to
|
||||
this bar component. You'll need to set up the item
|
||||
properties in a suitable way for a divider bar, e.g.
|
||||
for an 8-pixel wide bar which, you could call
|
||||
myLayout->setItemLayout (barIndex, 8, 8, 8)
|
||||
@param isBarVertical true if it's an upright bar that you drag left and
|
||||
right; false for a horizontal one that you drag up and
|
||||
down
|
||||
*/
|
||||
StretchableLayoutResizerBar (StretchableLayoutManager* layoutToUse,
|
||||
int itemIndexInLayout,
|
||||
bool isBarVertical);
|
||||
|
||||
/** Destructor. */
|
||||
~StretchableLayoutResizerBar() override;
|
||||
|
||||
//==============================================================================
|
||||
/** This is called when the bar is dragged.
|
||||
|
||||
This method must update the positions of any components whose position is
|
||||
determined by the StretchableLayoutManager, because they might have just
|
||||
moved.
|
||||
|
||||
The default implementation calls the resized() method of this component's
|
||||
parent component, because that's often where you're likely to apply the
|
||||
layout, but it can be overridden for more specific needs.
|
||||
*/
|
||||
virtual void hasBeenMoved();
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes. */
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual void drawStretchableLayoutResizerBar (Graphics&, int w, int h,
|
||||
bool isVerticalBar, bool isMouseOver, bool isMouseDragging) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
StretchableLayoutManager* layout;
|
||||
int itemIndex, mouseDownPos;
|
||||
bool isVertical;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableLayoutResizerBar)
|
||||
};
|
||||
|
||||
} // namespace juce
|
122
deps/juce/modules/juce_gui_basics/layout/juce_StretchableObjectResizer.cpp
vendored
Normal file
122
deps/juce/modules/juce_gui_basics/layout/juce_StretchableObjectResizer.cpp
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
StretchableObjectResizer::StretchableObjectResizer() {}
|
||||
StretchableObjectResizer::~StretchableObjectResizer() {}
|
||||
|
||||
void StretchableObjectResizer::addItem (const double size,
|
||||
const double minSize, const double maxSize,
|
||||
const int order)
|
||||
{
|
||||
// the order must be >= 0 but less than the maximum integer value.
|
||||
jassert (order >= 0 && order < std::numeric_limits<int>::max());
|
||||
jassert (maxSize >= minSize);
|
||||
|
||||
Item item;
|
||||
item.size = size;
|
||||
item.minSize = minSize;
|
||||
item.maxSize = maxSize;
|
||||
item.order = order;
|
||||
items.add (item);
|
||||
}
|
||||
|
||||
double StretchableObjectResizer::getItemSize (const int index) const noexcept
|
||||
{
|
||||
return isPositiveAndBelow (index, items.size()) ? items.getReference (index).size
|
||||
: 0.0;
|
||||
}
|
||||
|
||||
void StretchableObjectResizer::resizeToFit (const double targetSize)
|
||||
{
|
||||
int order = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
double currentSize = 0;
|
||||
double minSize = 0;
|
||||
double maxSize = 0;
|
||||
|
||||
int nextHighestOrder = std::numeric_limits<int>::max();
|
||||
|
||||
for (int i = 0; i < items.size(); ++i)
|
||||
{
|
||||
const Item& it = items.getReference(i);
|
||||
currentSize += it.size;
|
||||
|
||||
if (it.order <= order)
|
||||
{
|
||||
minSize += it.minSize;
|
||||
maxSize += it.maxSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
minSize += it.size;
|
||||
maxSize += it.size;
|
||||
nextHighestOrder = jmin (nextHighestOrder, it.order);
|
||||
}
|
||||
}
|
||||
|
||||
const double thisIterationTarget = jlimit (minSize, maxSize, targetSize);
|
||||
|
||||
if (thisIterationTarget >= currentSize)
|
||||
{
|
||||
const double availableExtraSpace = maxSize - currentSize;
|
||||
const double targetAmountOfExtraSpace = thisIterationTarget - currentSize;
|
||||
const double scale = availableExtraSpace > 0 ? targetAmountOfExtraSpace / availableExtraSpace : 1.0;
|
||||
|
||||
for (int i = 0; i < items.size(); ++i)
|
||||
{
|
||||
Item& it = items.getReference(i);
|
||||
|
||||
if (it.order <= order)
|
||||
it.size = jlimit (it.minSize, it.maxSize, it.size + (it.maxSize - it.size) * scale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const double amountOfSlack = currentSize - minSize;
|
||||
const double targetAmountOfSlack = thisIterationTarget - minSize;
|
||||
const double scale = targetAmountOfSlack / amountOfSlack;
|
||||
|
||||
for (int i = 0; i < items.size(); ++i)
|
||||
{
|
||||
Item& it = items.getReference(i);
|
||||
|
||||
if (it.order <= order)
|
||||
it.size = jmax (it.minSize, it.minSize + (it.size - it.minSize) * scale);
|
||||
}
|
||||
}
|
||||
|
||||
if (nextHighestOrder < std::numeric_limits<int>::max())
|
||||
order = nextHighestOrder;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
102
deps/juce/modules/juce_gui_basics/layout/juce_StretchableObjectResizer.h
vendored
Normal file
102
deps/juce/modules/juce_gui_basics/layout/juce_StretchableObjectResizer.h
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A utility class for fitting a set of objects whose sizes can vary between
|
||||
a minimum and maximum size, into a space.
|
||||
|
||||
This is a trickier algorithm than it would first seem, so I've put it in this
|
||||
class to allow it to be shared by various bits of code.
|
||||
|
||||
To use it, create one of these objects, call addItem() to add the list of items
|
||||
you need, then call resizeToFit(), which will change all their sizes. You can
|
||||
then retrieve the new sizes with getItemSize() and getNumItems().
|
||||
|
||||
It's currently used by the TableHeaderComponent for stretching out the table
|
||||
headings to fill the table's width.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class StretchableObjectResizer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty object resizer. */
|
||||
StretchableObjectResizer();
|
||||
|
||||
/** Destructor. */
|
||||
~StretchableObjectResizer();
|
||||
|
||||
//==============================================================================
|
||||
/** Adds an item to the list.
|
||||
|
||||
The order parameter lets you specify groups of items that are resized first when some
|
||||
space needs to be found. Those items with an order of 0 will be the first ones to be
|
||||
resized, and if that doesn't provide enough space to meet the requirements, the algorithm
|
||||
will then try resizing the items with an order of 1, then 2, and so on.
|
||||
*/
|
||||
void addItem (double currentSize,
|
||||
double minSize,
|
||||
double maxSize,
|
||||
int order = 0);
|
||||
|
||||
/** Resizes all the items to fit this amount of space.
|
||||
|
||||
This will attempt to fit them in without exceeding each item's minimum and
|
||||
maximum sizes. In cases where none of the items can be expanded or enlarged any
|
||||
further, the final size may be greater or less than the size passed in.
|
||||
|
||||
After calling this method, you can retrieve the new sizes with the getItemSize()
|
||||
method.
|
||||
*/
|
||||
void resizeToFit (double targetSize);
|
||||
|
||||
/** Returns the number of items that have been added. */
|
||||
int getNumItems() const noexcept { return items.size(); }
|
||||
|
||||
/** Returns the size of one of the items. */
|
||||
double getItemSize (int index) const noexcept;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct Item
|
||||
{
|
||||
double size;
|
||||
double minSize;
|
||||
double maxSize;
|
||||
int order;
|
||||
};
|
||||
|
||||
Array<Item> items;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableObjectResizer)
|
||||
};
|
||||
|
||||
} // namespace juce
|
591
deps/juce/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp
vendored
Normal file
591
deps/juce/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp
vendored
Normal file
@ -0,0 +1,591 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
TabBarButton::TabBarButton (const String& name, TabbedButtonBar& bar)
|
||||
: Button (name), owner (bar)
|
||||
{
|
||||
setWantsKeyboardFocus (false);
|
||||
}
|
||||
|
||||
TabBarButton::~TabBarButton() {}
|
||||
|
||||
int TabBarButton::getIndex() const { return owner.indexOfTabButton (this); }
|
||||
Colour TabBarButton::getTabBackgroundColour() const { return owner.getTabBackgroundColour (getIndex()); }
|
||||
bool TabBarButton::isFrontTab() const { return getToggleState(); }
|
||||
|
||||
void TabBarButton::paintButton (Graphics& g, const bool shouldDrawButtonAsHighlighted, const bool shouldDrawButtonAsDown)
|
||||
{
|
||||
getLookAndFeel().drawTabButton (*this, g, shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown);
|
||||
}
|
||||
|
||||
void TabBarButton::clicked (const ModifierKeys& mods)
|
||||
{
|
||||
if (mods.isPopupMenu())
|
||||
owner.popupMenuClickOnTab (getIndex(), getButtonText());
|
||||
else
|
||||
owner.setCurrentTabIndex (getIndex());
|
||||
}
|
||||
|
||||
bool TabBarButton::hitTest (int mx, int my)
|
||||
{
|
||||
auto area = getActiveArea();
|
||||
|
||||
if (owner.isVertical())
|
||||
{
|
||||
if (isPositiveAndBelow (mx, getWidth())
|
||||
&& my >= area.getY() + overlapPixels && my < area.getBottom() - overlapPixels)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isPositiveAndBelow (my, getHeight())
|
||||
&& mx >= area.getX() + overlapPixels && mx < area.getRight() - overlapPixels)
|
||||
return true;
|
||||
}
|
||||
|
||||
Path p;
|
||||
getLookAndFeel().createTabButtonShape (*this, p, false, false);
|
||||
|
||||
return p.contains ((float) (mx - area.getX()),
|
||||
(float) (my - area.getY()));
|
||||
}
|
||||
|
||||
int TabBarButton::getBestTabLength (const int depth)
|
||||
{
|
||||
return getLookAndFeel().getTabButtonBestWidth (*this, depth);
|
||||
}
|
||||
|
||||
void TabBarButton::calcAreas (Rectangle<int>& extraComp, Rectangle<int>& textArea) const
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
textArea = getActiveArea();
|
||||
|
||||
auto depth = owner.isVertical() ? textArea.getWidth() : textArea.getHeight();
|
||||
auto overlap = lf.getTabButtonOverlap (depth);
|
||||
|
||||
if (overlap > 0)
|
||||
{
|
||||
if (owner.isVertical())
|
||||
textArea.reduce (0, overlap);
|
||||
else
|
||||
textArea.reduce (overlap, 0);
|
||||
}
|
||||
|
||||
if (extraComponent != nullptr)
|
||||
{
|
||||
extraComp = lf.getTabButtonExtraComponentBounds (*this, textArea, *extraComponent);
|
||||
|
||||
auto orientation = owner.getOrientation();
|
||||
|
||||
if (orientation == TabbedButtonBar::TabsAtLeft || orientation == TabbedButtonBar::TabsAtRight)
|
||||
{
|
||||
if (extraComp.getCentreY() > textArea.getCentreY())
|
||||
textArea.setBottom (jmin (textArea.getBottom(), extraComp.getY()));
|
||||
else
|
||||
textArea.setTop (jmax (textArea.getY(), extraComp.getBottom()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (extraCompPlacement == afterText || extraCompPlacement == beforeText) {
|
||||
if (extraComp.getCentreX() > textArea.getCentreX())
|
||||
textArea.setRight (jmin (textArea.getRight(), extraComp.getX()));
|
||||
else
|
||||
textArea.setLeft (jmax (textArea.getX(), extraComp.getRight()));
|
||||
}
|
||||
else {
|
||||
if (extraComp.getCentreY() > textArea.getCentreY())
|
||||
textArea.setBottom (jmin (textArea.getBottom(), extraComp.getY()));
|
||||
else
|
||||
textArea.setTop (jmax (textArea.getY(), extraComp.getBottom()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle<int> TabBarButton::getTextArea() const
|
||||
{
|
||||
Rectangle<int> extraComp, textArea;
|
||||
calcAreas (extraComp, textArea);
|
||||
return textArea;
|
||||
}
|
||||
|
||||
Rectangle<int> TabBarButton::getActiveArea() const
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
auto spaceAroundImage = getLookAndFeel().getTabButtonSpaceAroundImage();
|
||||
auto orientation = owner.getOrientation();
|
||||
|
||||
if (orientation != TabbedButtonBar::TabsAtLeft) r.removeFromRight (spaceAroundImage);
|
||||
if (orientation != TabbedButtonBar::TabsAtRight) r.removeFromLeft (spaceAroundImage);
|
||||
if (orientation != TabbedButtonBar::TabsAtBottom) r.removeFromTop (spaceAroundImage);
|
||||
if (orientation != TabbedButtonBar::TabsAtTop) r.removeFromBottom (spaceAroundImage);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void TabBarButton::setExtraComponent (Component* comp, ExtraComponentPlacement placement)
|
||||
{
|
||||
jassert (extraCompPlacement == beforeText || extraCompPlacement == afterText || extraCompPlacement == belowText || extraCompPlacement == aboveText);
|
||||
extraCompPlacement = placement;
|
||||
extraComponent.reset (comp);
|
||||
addAndMakeVisible (extraComponent.get());
|
||||
resized();
|
||||
}
|
||||
|
||||
void TabBarButton::childBoundsChanged (Component* c)
|
||||
{
|
||||
if (c == extraComponent.get())
|
||||
{
|
||||
owner.resized();
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
void TabBarButton::resized()
|
||||
{
|
||||
if (extraComponent != nullptr)
|
||||
{
|
||||
Rectangle<int> extraComp, textArea;
|
||||
calcAreas (extraComp, textArea);
|
||||
|
||||
if (! extraComp.isEmpty())
|
||||
extraComponent->setBounds (extraComp);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class TabbedButtonBar::BehindFrontTabComp : public Component
|
||||
{
|
||||
public:
|
||||
BehindFrontTabComp (TabbedButtonBar& tb) : owner (tb)
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
getLookAndFeel().drawTabAreaBehindFrontButton (owner, g, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
void enablementChanged() override
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
TabbedButtonBar& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (BehindFrontTabComp)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
TabbedButtonBar::TabbedButtonBar (Orientation orientationToUse)
|
||||
: orientation (orientationToUse)
|
||||
{
|
||||
setInterceptsMouseClicks (false, true);
|
||||
behindFrontTab.reset (new BehindFrontTabComp (*this));
|
||||
addAndMakeVisible (behindFrontTab.get());
|
||||
setFocusContainerType (FocusContainerType::keyboardFocusContainer);
|
||||
}
|
||||
|
||||
TabbedButtonBar::~TabbedButtonBar()
|
||||
{
|
||||
tabs.clear();
|
||||
extraTabsButton.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TabbedButtonBar::setOrientation (const Orientation newOrientation)
|
||||
{
|
||||
orientation = newOrientation;
|
||||
|
||||
for (auto* child : getChildren())
|
||||
child->resized();
|
||||
|
||||
resized();
|
||||
}
|
||||
|
||||
TabBarButton* TabbedButtonBar::createTabButton (const String& name, const int /*index*/)
|
||||
{
|
||||
return new TabBarButton (name, *this);
|
||||
}
|
||||
|
||||
void TabbedButtonBar::setMinimumTabScaleFactor (double newMinimumScale)
|
||||
{
|
||||
minimumScale = newMinimumScale;
|
||||
resized();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TabbedButtonBar::clearTabs()
|
||||
{
|
||||
tabs.clear();
|
||||
extraTabsButton.reset();
|
||||
setCurrentTabIndex (-1);
|
||||
}
|
||||
|
||||
void TabbedButtonBar::addTab (const String& tabName,
|
||||
Colour tabBackgroundColour,
|
||||
int insertIndex)
|
||||
{
|
||||
jassert (tabName.isNotEmpty()); // you have to give them all a name..
|
||||
|
||||
if (tabName.isNotEmpty())
|
||||
{
|
||||
if (! isPositiveAndBelow (insertIndex, tabs.size()))
|
||||
insertIndex = tabs.size();
|
||||
|
||||
auto* currentTab = tabs[currentTabIndex];
|
||||
|
||||
auto* newTab = new TabInfo();
|
||||
newTab->name = tabName;
|
||||
newTab->colour = tabBackgroundColour;
|
||||
newTab->button.reset (createTabButton (tabName, insertIndex));
|
||||
jassert (newTab->button != nullptr);
|
||||
|
||||
tabs.insert (insertIndex, newTab);
|
||||
currentTabIndex = tabs.indexOf (currentTab);
|
||||
addAndMakeVisible (newTab->button.get(), insertIndex);
|
||||
|
||||
resized();
|
||||
|
||||
if (currentTabIndex < 0)
|
||||
setCurrentTabIndex (0);
|
||||
}
|
||||
}
|
||||
|
||||
void TabbedButtonBar::setTabName (int tabIndex, const String& newName)
|
||||
{
|
||||
if (auto* tab = tabs[tabIndex])
|
||||
{
|
||||
if (tab->name != newName)
|
||||
{
|
||||
tab->name = newName;
|
||||
tab->button->setButtonText (newName);
|
||||
resized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabbedButtonBar::removeTab (const int indexToRemove, const bool animate)
|
||||
{
|
||||
if (isPositiveAndBelow (indexToRemove, tabs.size()))
|
||||
{
|
||||
auto oldSelectedIndex = currentTabIndex;
|
||||
|
||||
if (indexToRemove == currentTabIndex)
|
||||
oldSelectedIndex = -1;
|
||||
else if (indexToRemove < oldSelectedIndex)
|
||||
--oldSelectedIndex;
|
||||
|
||||
tabs.remove (indexToRemove);
|
||||
|
||||
setCurrentTabIndex (oldSelectedIndex);
|
||||
updateTabPositions (animate);
|
||||
}
|
||||
}
|
||||
|
||||
void TabbedButtonBar::moveTab (const int currentIndex, const int newIndex, const bool animate)
|
||||
{
|
||||
auto* currentTab = tabs[currentTabIndex];
|
||||
tabs.move (currentIndex, newIndex);
|
||||
currentTabIndex = tabs.indexOf (currentTab);
|
||||
updateTabPositions (animate);
|
||||
}
|
||||
|
||||
int TabbedButtonBar::getNumTabs() const
|
||||
{
|
||||
return tabs.size();
|
||||
}
|
||||
|
||||
String TabbedButtonBar::getCurrentTabName() const
|
||||
{
|
||||
if (auto* tab = tabs [currentTabIndex])
|
||||
return tab->name;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StringArray TabbedButtonBar::getTabNames() const
|
||||
{
|
||||
StringArray names;
|
||||
|
||||
for (auto* t : tabs)
|
||||
names.add (t->name);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
void TabbedButtonBar::setCurrentTabIndex (int newIndex, bool shouldSendChangeMessage)
|
||||
{
|
||||
if (currentTabIndex != newIndex)
|
||||
{
|
||||
if (! isPositiveAndBelow (newIndex, tabs.size()))
|
||||
newIndex = -1;
|
||||
|
||||
currentTabIndex = newIndex;
|
||||
|
||||
for (int i = 0; i < tabs.size(); ++i)
|
||||
tabs.getUnchecked(i)->button->setToggleState (i == newIndex, dontSendNotification);
|
||||
|
||||
resized();
|
||||
|
||||
if (shouldSendChangeMessage)
|
||||
sendChangeMessage();
|
||||
|
||||
currentTabChanged (newIndex, getCurrentTabName());
|
||||
}
|
||||
}
|
||||
|
||||
TabBarButton* TabbedButtonBar::getTabButton (const int index) const
|
||||
{
|
||||
if (auto* tab = tabs[index])
|
||||
return static_cast<TabBarButton*> (tab->button.get());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int TabbedButtonBar::indexOfTabButton (const TabBarButton* button) const
|
||||
{
|
||||
for (int i = tabs.size(); --i >= 0;)
|
||||
if (tabs.getUnchecked(i)->button.get() == button)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Rectangle<int> TabbedButtonBar::getTargetBounds (TabBarButton* button) const
|
||||
{
|
||||
if (button == nullptr || indexOfTabButton (button) == -1)
|
||||
return {};
|
||||
|
||||
auto& animator = Desktop::getInstance().getAnimator();
|
||||
|
||||
return animator.isAnimating (button) ? animator.getComponentDestination (button)
|
||||
: button->getBounds();
|
||||
}
|
||||
|
||||
void TabbedButtonBar::lookAndFeelChanged()
|
||||
{
|
||||
extraTabsButton.reset();
|
||||
resized();
|
||||
}
|
||||
|
||||
void TabbedButtonBar::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawTabbedButtonBarBackground (*this, g);
|
||||
}
|
||||
|
||||
void TabbedButtonBar::resized()
|
||||
{
|
||||
updateTabPositions (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TabbedButtonBar::updateTabPositions (bool animate)
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
auto depth = getWidth();
|
||||
auto length = getHeight();
|
||||
|
||||
if (! isVertical())
|
||||
std::swap (depth, length);
|
||||
|
||||
auto overlap = lf.getTabButtonOverlap (depth) + lf.getTabButtonSpaceAroundImage() * 2;
|
||||
|
||||
auto totalLength = jmax (0, overlap);
|
||||
auto numVisibleButtons = tabs.size();
|
||||
|
||||
for (int i = 0; i < tabs.size(); ++i)
|
||||
{
|
||||
auto* tb = tabs.getUnchecked(i)->button.get();
|
||||
|
||||
totalLength += tb->getBestTabLength (depth) - overlap;
|
||||
tb->overlapPixels = jmax (0, overlap / 2);
|
||||
}
|
||||
|
||||
double scale = 1.0;
|
||||
|
||||
if (totalLength > length)
|
||||
scale = jmax (minimumScale, length / (double) totalLength);
|
||||
|
||||
const bool isTooBig = (int) (totalLength * scale) > length;
|
||||
int tabsButtonPos = 0;
|
||||
|
||||
if (isTooBig)
|
||||
{
|
||||
if (extraTabsButton == nullptr)
|
||||
{
|
||||
extraTabsButton.reset (lf.createTabBarExtrasButton());
|
||||
addAndMakeVisible (extraTabsButton.get());
|
||||
extraTabsButton->setAlwaysOnTop (true);
|
||||
extraTabsButton->setTriggeredOnMouseDown (true);
|
||||
extraTabsButton->onClick = [this] { showExtraItemsMenu(); };
|
||||
}
|
||||
|
||||
auto buttonSize = jmin (proportionOfWidth (0.7f), proportionOfHeight (0.7f));
|
||||
extraTabsButton->setSize (buttonSize, buttonSize);
|
||||
|
||||
if (isVertical())
|
||||
{
|
||||
tabsButtonPos = getHeight() - buttonSize / 2 - 1;
|
||||
extraTabsButton->setCentrePosition (getWidth() / 2, tabsButtonPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
tabsButtonPos = getWidth() - buttonSize / 2 - 1;
|
||||
extraTabsButton->setCentrePosition (tabsButtonPos, getHeight() / 2);
|
||||
}
|
||||
|
||||
totalLength = 0;
|
||||
|
||||
for (int i = 0; i < tabs.size(); ++i)
|
||||
{
|
||||
auto* tb = tabs.getUnchecked(i)->button.get();
|
||||
auto newLength = totalLength + tb->getBestTabLength (depth);
|
||||
|
||||
if (i > 0 && newLength * minimumScale > tabsButtonPos)
|
||||
{
|
||||
totalLength += overlap;
|
||||
break;
|
||||
}
|
||||
|
||||
numVisibleButtons = i + 1;
|
||||
totalLength = newLength - overlap;
|
||||
}
|
||||
|
||||
scale = jmax (minimumScale, tabsButtonPos / (double) totalLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
extraTabsButton.reset();
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
|
||||
TabBarButton* frontTab = nullptr;
|
||||
auto& animator = Desktop::getInstance().getAnimator();
|
||||
|
||||
for (int i = 0; i < tabs.size(); ++i)
|
||||
{
|
||||
if (auto* tb = getTabButton (i))
|
||||
{
|
||||
auto bestLength = roundToInt (scale * tb->getBestTabLength (depth));
|
||||
|
||||
if (i < numVisibleButtons)
|
||||
{
|
||||
auto newBounds = isVertical() ? Rectangle<int> (0, pos, getWidth(), bestLength)
|
||||
: Rectangle<int> (pos, 0, bestLength, getHeight());
|
||||
|
||||
if (animate)
|
||||
{
|
||||
animator.animateComponent (tb, newBounds, 1.0f, 200, false, 3.0, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
animator.cancelAnimation (tb, false);
|
||||
tb->setBounds (newBounds);
|
||||
}
|
||||
|
||||
tb->toBack();
|
||||
|
||||
if (i == currentTabIndex)
|
||||
frontTab = tb;
|
||||
|
||||
tb->setVisible (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
tb->setVisible (false);
|
||||
}
|
||||
|
||||
pos += bestLength - overlap;
|
||||
}
|
||||
}
|
||||
|
||||
behindFrontTab->setBounds (getLocalBounds());
|
||||
|
||||
if (frontTab != nullptr)
|
||||
{
|
||||
frontTab->toFront (false);
|
||||
behindFrontTab->toBehind (frontTab);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Colour TabbedButtonBar::getTabBackgroundColour (int tabIndex)
|
||||
{
|
||||
if (auto* tab = tabs[tabIndex])
|
||||
return tab->colour;
|
||||
|
||||
return Colours::transparentBlack;
|
||||
}
|
||||
|
||||
void TabbedButtonBar::setTabBackgroundColour (int tabIndex, Colour newColour)
|
||||
{
|
||||
if (auto* tab = tabs [tabIndex])
|
||||
{
|
||||
if (tab->colour != newColour)
|
||||
{
|
||||
tab->colour = newColour;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabbedButtonBar::showExtraItemsMenu()
|
||||
{
|
||||
PopupMenu m;
|
||||
|
||||
for (int i = 0; i < tabs.size(); ++i)
|
||||
{
|
||||
auto* tab = tabs.getUnchecked(i);
|
||||
|
||||
if (! tab->button->isVisible())
|
||||
m.addItem (PopupMenu::Item (tab->name)
|
||||
.setTicked (i == currentTabIndex)
|
||||
.setAction ([this, i] { setCurrentTabIndex (i); }));
|
||||
}
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options()
|
||||
.withDeletionCheck (*this)
|
||||
.withTargetComponent (extraTabsButton.get()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TabbedButtonBar::currentTabChanged (int, const String&) {}
|
||||
void TabbedButtonBar::popupMenuClickOnTab (int, const String&) {}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> TabbedButtonBar::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
|
||||
}
|
||||
|
||||
} // namespace juce
|
374
deps/juce/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h
vendored
Normal file
374
deps/juce/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h
vendored
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class TabbedButtonBar;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** In a TabbedButtonBar, this component is used for each of the buttons.
|
||||
|
||||
If you want to create a TabbedButtonBar with custom tab components, derive
|
||||
your component from this class, and override the TabbedButtonBar::createTabButton()
|
||||
method to create it instead of the default one.
|
||||
|
||||
@see TabbedButtonBar
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API TabBarButton : public Button
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates the tab button. */
|
||||
TabBarButton (const String& name, TabbedButtonBar& ownerBar);
|
||||
|
||||
/** Destructor. */
|
||||
~TabBarButton() override;
|
||||
|
||||
/** Returns the bar that contains this button. */
|
||||
TabbedButtonBar& getTabbedButtonBar() const { return owner; }
|
||||
|
||||
//==============================================================================
|
||||
/** When adding an extra component to the tab, this indicates which side of
|
||||
the text it should be placed on. */
|
||||
enum ExtraComponentPlacement
|
||||
{
|
||||
beforeText,
|
||||
afterText,
|
||||
aboveText,
|
||||
belowText
|
||||
};
|
||||
|
||||
/** Sets an extra component that will be shown in the tab.
|
||||
|
||||
This optional component will be positioned inside the tab, either to the left or right
|
||||
of the text. You could use this to implement things like a close button or a graphical
|
||||
status indicator. If a non-null component is passed-in, the TabbedButtonBar will take
|
||||
ownership of it and delete it when required.
|
||||
*/
|
||||
void setExtraComponent (Component* extraTabComponent,
|
||||
ExtraComponentPlacement extraComponentPlacement);
|
||||
|
||||
/** Returns the custom component, if there is one. */
|
||||
Component* getExtraComponent() const noexcept { return extraComponent.get(); }
|
||||
|
||||
/** Returns the placement of the custom component, if there is one. */
|
||||
ExtraComponentPlacement getExtraComponentPlacement() const noexcept { return extraCompPlacement; }
|
||||
|
||||
/** Returns an area of the component that's safe to draw in.
|
||||
|
||||
This deals with the orientation of the tabs, which affects which side is
|
||||
touching the tabbed box's content component.
|
||||
*/
|
||||
Rectangle<int> getActiveArea() const;
|
||||
|
||||
/** Returns the area of the component that should contain its text. */
|
||||
Rectangle<int> getTextArea() const;
|
||||
|
||||
/** Returns this tab's index in its tab bar. */
|
||||
int getIndex() const;
|
||||
|
||||
/** Returns the colour of the tab. */
|
||||
Colour getTabBackgroundColour() const;
|
||||
|
||||
/** Returns true if this is the frontmost (selected) tab. */
|
||||
bool isFrontTab() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Chooses the best length for the tab, given the specified depth.
|
||||
|
||||
If the tab is horizontal, this should return its width, and the depth
|
||||
specifies its height. If it's vertical, it should return the height, and
|
||||
the depth is actually its width.
|
||||
*/
|
||||
virtual int getBestTabLength (int depth);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paintButton (Graphics&, bool, bool) override;
|
||||
/** @internal */
|
||||
void clicked (const ModifierKeys&) override;
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void childBoundsChanged (Component*) override;
|
||||
|
||||
protected:
|
||||
friend class TabbedButtonBar;
|
||||
TabbedButtonBar& owner;
|
||||
int overlapPixels = 0;
|
||||
|
||||
std::unique_ptr<Component> extraComponent;
|
||||
ExtraComponentPlacement extraCompPlacement = afterText;
|
||||
|
||||
private:
|
||||
using Button::clicked;
|
||||
void calcAreas (Rectangle<int>&, Rectangle<int>&) const;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabBarButton)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A vertical or horizontal bar containing tabs that you can select.
|
||||
|
||||
You can use one of these to generate things like a dialog box that has
|
||||
tabbed pages you can flip between. Attach a ChangeListener to the
|
||||
button bar to be told when the user changes the page.
|
||||
|
||||
An easier method than doing this is to use a TabbedComponent, which
|
||||
contains its own TabbedButtonBar and which takes care of the layout
|
||||
and other housekeeping.
|
||||
|
||||
@see TabbedComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API TabbedButtonBar : public Component,
|
||||
public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** The placement of the tab-bar
|
||||
@see setOrientation, getOrientation
|
||||
*/
|
||||
enum Orientation
|
||||
{
|
||||
TabsAtTop,
|
||||
TabsAtBottom,
|
||||
TabsAtLeft,
|
||||
TabsAtRight
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a TabbedButtonBar with a given orientation.
|
||||
You can change the orientation later if you need to.
|
||||
*/
|
||||
TabbedButtonBar (Orientation orientation);
|
||||
|
||||
/** Destructor. */
|
||||
~TabbedButtonBar() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the bar's orientation.
|
||||
|
||||
This won't change the bar's actual size - you'll need to do that yourself,
|
||||
but this determines which direction the tabs go in, and which side they're
|
||||
stuck to.
|
||||
*/
|
||||
void setOrientation (Orientation orientation);
|
||||
|
||||
/** Returns the bar's current orientation.
|
||||
@see setOrientation
|
||||
*/
|
||||
Orientation getOrientation() const noexcept { return orientation; }
|
||||
|
||||
/** Returns true if the orientation is TabsAtLeft or TabsAtRight. */
|
||||
bool isVertical() const noexcept { return orientation == TabsAtLeft || orientation == TabsAtRight; }
|
||||
|
||||
/** Returns the thickness of the bar, which may be its width or height, depending on the orientation. */
|
||||
int getThickness() const noexcept { return isVertical() ? getWidth() : getHeight(); }
|
||||
|
||||
/** Changes the minimum scale factor to which the tabs can be compressed when trying to
|
||||
fit a lot of tabs on-screen.
|
||||
*/
|
||||
void setMinimumTabScaleFactor (double newMinimumScale);
|
||||
|
||||
//==============================================================================
|
||||
/** Deletes all the tabs from the bar.
|
||||
@see addTab
|
||||
*/
|
||||
void clearTabs();
|
||||
|
||||
/** Adds a tab to the bar.
|
||||
Tabs are added in left-to-right reading order.
|
||||
If this is the first tab added, it'll also be automatically selected.
|
||||
*/
|
||||
void addTab (const String& tabName,
|
||||
Colour tabBackgroundColour,
|
||||
int insertIndex);
|
||||
|
||||
/** Changes the name of one of the tabs. */
|
||||
void setTabName (int tabIndex, const String& newName);
|
||||
|
||||
/** Gets rid of one of the tabs. */
|
||||
void removeTab (int tabIndex, bool animate = false);
|
||||
|
||||
/** Moves a tab to a new index in the list.
|
||||
Pass -1 as the index to move it to the end of the list.
|
||||
*/
|
||||
void moveTab (int currentIndex, int newIndex, bool animate = false);
|
||||
|
||||
/** Returns the number of tabs in the bar. */
|
||||
int getNumTabs() const;
|
||||
|
||||
/** Returns a list of all the tab names in the bar. */
|
||||
StringArray getTabNames() const;
|
||||
|
||||
/** Changes the currently selected tab.
|
||||
This will send a change message and cause a synchronous callback to
|
||||
the currentTabChanged() method. (But if the given tab is already selected,
|
||||
nothing will be done).
|
||||
|
||||
To deselect all the tabs, use an index of -1.
|
||||
*/
|
||||
void setCurrentTabIndex (int newTabIndex, bool sendChangeMessage = true);
|
||||
|
||||
/** Returns the name of the currently selected tab.
|
||||
This could be an empty string if none are selected.
|
||||
*/
|
||||
String getCurrentTabName() const;
|
||||
|
||||
/** Returns the index of the currently selected tab.
|
||||
This could return -1 if none are selected.
|
||||
*/
|
||||
int getCurrentTabIndex() const noexcept { return currentTabIndex; }
|
||||
|
||||
/** Returns the button for a specific tab.
|
||||
The button that is returned may be deleted later by this component, so don't hang
|
||||
on to the pointer that is returned. A null pointer may be returned if the index is
|
||||
out of range.
|
||||
*/
|
||||
TabBarButton* getTabButton (int index) const;
|
||||
|
||||
/** Returns the index of a TabBarButton if it belongs to this bar. */
|
||||
int indexOfTabButton (const TabBarButton* button) const;
|
||||
|
||||
/** Returns the final bounds of this button if it is currently being animated. */
|
||||
Rectangle<int> getTargetBounds (TabBarButton* button) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Callback method to indicate the selected tab has been changed.
|
||||
@see setCurrentTabIndex
|
||||
*/
|
||||
virtual void currentTabChanged (int newCurrentTabIndex,
|
||||
const String& newCurrentTabName);
|
||||
|
||||
/** Callback method to indicate that the user has right-clicked on a tab. */
|
||||
virtual void popupMenuClickOnTab (int tabIndex, const String& tabName);
|
||||
|
||||
/** Returns the colour of a tab.
|
||||
This is the colour that was specified in addTab().
|
||||
*/
|
||||
Colour getTabBackgroundColour (int tabIndex);
|
||||
|
||||
/** Changes the background colour of a tab.
|
||||
@see addTab, getTabBackgroundColour
|
||||
*/
|
||||
void setTabBackgroundColour (int tabIndex, Colour newColour);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the component.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
tabOutlineColourId = 0x1005812, /**< The colour to use to draw an outline around the tabs. */
|
||||
tabTextColourId = 0x1005813, /**< The colour to use to draw the tab names. If this isn't specified,
|
||||
the look and feel will choose an appropriate colour. */
|
||||
frontOutlineColourId = 0x1005814, /**< The colour to use to draw an outline around the currently-selected tab. */
|
||||
frontTextColourId = 0x1005815, /**< The colour to use to draw the currently-selected tab name. If
|
||||
this isn't specified, the look and feel will choose an appropriate
|
||||
colour. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
window drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual int getTabButtonSpaceAroundImage() = 0;
|
||||
virtual int getTabButtonOverlap (int tabDepth) = 0;
|
||||
virtual int getTabButtonBestWidth (TabBarButton&, int tabDepth) = 0;
|
||||
virtual Rectangle<int> getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle<int>& textArea, Component& extraComp) = 0;
|
||||
|
||||
virtual void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
|
||||
virtual Font getTabButtonFont (TabBarButton&, float height) = 0;
|
||||
virtual void drawTabButtonText (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
|
||||
virtual void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) = 0;
|
||||
virtual void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int w, int h) = 0;
|
||||
|
||||
virtual void createTabButtonShape (TabBarButton&, Path& path, bool isMouseOver, bool isMouseDown) = 0;
|
||||
virtual void fillTabButtonShape (TabBarButton&, Graphics&, const Path& path, bool isMouseOver, bool isMouseDown) = 0;
|
||||
|
||||
virtual Button* createTabBarExtrasButton() = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** This creates one of the tabs.
|
||||
|
||||
If you need to use custom tab components, you can override this method and
|
||||
return your own class instead of the default.
|
||||
*/
|
||||
virtual TabBarButton* createTabButton (const String& tabName, int tabIndex);
|
||||
|
||||
private:
|
||||
struct TabInfo
|
||||
{
|
||||
std::unique_ptr<TabBarButton> button;
|
||||
String name;
|
||||
Colour colour;
|
||||
};
|
||||
|
||||
OwnedArray<TabInfo> tabs;
|
||||
|
||||
Orientation orientation;
|
||||
double minimumScale = 0.7;
|
||||
int currentTabIndex = -1;
|
||||
|
||||
class BehindFrontTabComp;
|
||||
std::unique_ptr<BehindFrontTabComp> behindFrontTab;
|
||||
std::unique_ptr<Button> extraTabsButton;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void showExtraItemsMenu();
|
||||
void updateTabPositions (bool animate);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedButtonBar)
|
||||
};
|
||||
|
||||
} // namespace juce
|
324
deps/juce/modules/juce_gui_basics/layout/juce_TabbedComponent.cpp
vendored
Normal file
324
deps/juce/modules/juce_gui_basics/layout/juce_TabbedComponent.cpp
vendored
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace TabbedComponentHelpers
|
||||
{
|
||||
const Identifier deleteComponentId ("deleteByTabComp_");
|
||||
|
||||
static void deleteIfNecessary (Component* comp)
|
||||
{
|
||||
if (comp != nullptr && (bool) comp->getProperties() [deleteComponentId])
|
||||
delete comp;
|
||||
}
|
||||
|
||||
static Rectangle<int> getTabArea (Rectangle<int>& content, BorderSize<int>& outline,
|
||||
TabbedButtonBar::Orientation orientation, int tabDepth)
|
||||
{
|
||||
switch (orientation)
|
||||
{
|
||||
case TabbedButtonBar::TabsAtTop: outline.setTop (0); return content.removeFromTop (tabDepth);
|
||||
case TabbedButtonBar::TabsAtBottom: outline.setBottom (0); return content.removeFromBottom (tabDepth);
|
||||
case TabbedButtonBar::TabsAtLeft: outline.setLeft (0); return content.removeFromLeft (tabDepth);
|
||||
case TabbedButtonBar::TabsAtRight: outline.setRight (0); return content.removeFromRight (tabDepth);
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
|
||||
return Rectangle<int>();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct TabbedComponent::ButtonBar : public TabbedButtonBar
|
||||
{
|
||||
ButtonBar (TabbedComponent& tabComp, TabbedButtonBar::Orientation o)
|
||||
: TabbedButtonBar (o), owner (tabComp)
|
||||
{
|
||||
}
|
||||
|
||||
void currentTabChanged (int newCurrentTabIndex, const String& newTabName)
|
||||
{
|
||||
owner.changeCallback (newCurrentTabIndex, newTabName);
|
||||
}
|
||||
|
||||
void popupMenuClickOnTab (int tabIndex, const String& tabName)
|
||||
{
|
||||
owner.popupMenuClickOnTab (tabIndex, tabName);
|
||||
}
|
||||
|
||||
Colour getTabBackgroundColour (int tabIndex)
|
||||
{
|
||||
return owner.tabs->getTabBackgroundColour (tabIndex);
|
||||
}
|
||||
|
||||
TabBarButton* createTabButton (const String& tabName, int tabIndex)
|
||||
{
|
||||
return owner.createTabButton (tabName, tabIndex);
|
||||
}
|
||||
|
||||
TabbedComponent& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonBar)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
TabbedComponent::TabbedComponent (TabbedButtonBar::Orientation orientation)
|
||||
{
|
||||
tabs.reset (new ButtonBar (*this, orientation));
|
||||
addAndMakeVisible (tabs.get());
|
||||
}
|
||||
|
||||
TabbedComponent::~TabbedComponent()
|
||||
{
|
||||
clearTabs();
|
||||
tabs.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TabbedComponent::setOrientation (TabbedButtonBar::Orientation orientation)
|
||||
{
|
||||
tabs->setOrientation (orientation);
|
||||
resized();
|
||||
}
|
||||
|
||||
TabbedButtonBar::Orientation TabbedComponent::getOrientation() const noexcept
|
||||
{
|
||||
return tabs->getOrientation();
|
||||
}
|
||||
|
||||
void TabbedComponent::setTabBarDepth (int newDepth)
|
||||
{
|
||||
if (tabDepth != newDepth)
|
||||
{
|
||||
tabDepth = newDepth;
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
TabBarButton* TabbedComponent::createTabButton (const String& tabName, int /*tabIndex*/)
|
||||
{
|
||||
return new TabBarButton (tabName, *tabs);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TabbedComponent::clearTabs()
|
||||
{
|
||||
if (panelComponent != nullptr)
|
||||
{
|
||||
panelComponent->setVisible (false);
|
||||
removeChildComponent (panelComponent.get());
|
||||
panelComponent = nullptr;
|
||||
}
|
||||
|
||||
tabs->clearTabs();
|
||||
|
||||
for (int i = contentComponents.size(); --i >= 0;)
|
||||
TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (i));
|
||||
|
||||
contentComponents.clear();
|
||||
}
|
||||
|
||||
void TabbedComponent::addTab (const String& tabName,
|
||||
Colour tabBackgroundColour,
|
||||
Component* contentComponent,
|
||||
bool deleteComponentWhenNotNeeded,
|
||||
int insertIndex)
|
||||
{
|
||||
contentComponents.insert (insertIndex, WeakReference<Component> (contentComponent));
|
||||
|
||||
if (deleteComponentWhenNotNeeded && contentComponent != nullptr)
|
||||
contentComponent->getProperties().set (TabbedComponentHelpers::deleteComponentId, true);
|
||||
|
||||
tabs->addTab (tabName, tabBackgroundColour, insertIndex);
|
||||
resized();
|
||||
}
|
||||
|
||||
void TabbedComponent::setTabDeleteComponentWhenNotNeeded (int tabIndex, bool flag)
|
||||
{
|
||||
contentComponents [tabIndex]->getProperties().set (TabbedComponentHelpers::deleteComponentId, flag);
|
||||
}
|
||||
|
||||
void TabbedComponent::setTabName (int tabIndex, const String& newName)
|
||||
{
|
||||
tabs->setTabName (tabIndex, newName);
|
||||
}
|
||||
|
||||
void TabbedComponent::removeTab (int tabIndex)
|
||||
{
|
||||
if (isPositiveAndBelow (tabIndex, contentComponents.size()))
|
||||
{
|
||||
TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (tabIndex).get());
|
||||
contentComponents.remove (tabIndex);
|
||||
tabs->removeTab (tabIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void TabbedComponent::moveTab (int currentIndex, int newIndex, bool animate)
|
||||
{
|
||||
contentComponents.move (currentIndex, newIndex);
|
||||
tabs->moveTab (currentIndex, newIndex, animate);
|
||||
}
|
||||
|
||||
int TabbedComponent::getNumTabs() const
|
||||
{
|
||||
return tabs->getNumTabs();
|
||||
}
|
||||
|
||||
StringArray TabbedComponent::getTabNames() const
|
||||
{
|
||||
return tabs->getTabNames();
|
||||
}
|
||||
|
||||
Component* TabbedComponent::getTabContentComponent (int tabIndex) const noexcept
|
||||
{
|
||||
return contentComponents[tabIndex].get();
|
||||
}
|
||||
|
||||
Colour TabbedComponent::getTabBackgroundColour (int tabIndex) const noexcept
|
||||
{
|
||||
return tabs->getTabBackgroundColour (tabIndex);
|
||||
}
|
||||
|
||||
void TabbedComponent::setTabBackgroundColour (int tabIndex, Colour newColour)
|
||||
{
|
||||
tabs->setTabBackgroundColour (tabIndex, newColour);
|
||||
|
||||
if (getCurrentTabIndex() == tabIndex)
|
||||
repaint();
|
||||
}
|
||||
|
||||
void TabbedComponent::setCurrentTabIndex (int newTabIndex, bool sendChangeMessage)
|
||||
{
|
||||
tabs->setCurrentTabIndex (newTabIndex, sendChangeMessage);
|
||||
}
|
||||
|
||||
int TabbedComponent::getCurrentTabIndex() const
|
||||
{
|
||||
return tabs->getCurrentTabIndex();
|
||||
}
|
||||
|
||||
String TabbedComponent::getCurrentTabName() const
|
||||
{
|
||||
return tabs->getCurrentTabName();
|
||||
}
|
||||
|
||||
void TabbedComponent::setOutline (int thickness)
|
||||
{
|
||||
outlineThickness = thickness;
|
||||
resized();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void TabbedComponent::setIndent (int indentThickness)
|
||||
{
|
||||
edgeIndent = indentThickness;
|
||||
resized();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void TabbedComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
|
||||
auto content = getLocalBounds();
|
||||
BorderSize<int> outline (outlineThickness);
|
||||
TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth);
|
||||
|
||||
g.reduceClipRegion (content);
|
||||
g.fillAll (tabs->getTabBackgroundColour (getCurrentTabIndex()));
|
||||
|
||||
if (outlineThickness > 0)
|
||||
{
|
||||
RectangleList<int> rl (content);
|
||||
rl.subtract (outline.subtractedFrom (content));
|
||||
|
||||
g.reduceClipRegion (rl);
|
||||
g.fillAll (findColour (outlineColourId));
|
||||
}
|
||||
}
|
||||
|
||||
void TabbedComponent::resized()
|
||||
{
|
||||
auto content = getLocalBounds();
|
||||
BorderSize<int> outline (outlineThickness);
|
||||
|
||||
tabs->setBounds (TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth));
|
||||
content = BorderSize<int> (edgeIndent).subtractedFrom (outline.subtractedFrom (content));
|
||||
|
||||
for (auto& c : contentComponents)
|
||||
if (auto comp = c.get())
|
||||
comp->setBounds (content);
|
||||
}
|
||||
|
||||
void TabbedComponent::lookAndFeelChanged()
|
||||
{
|
||||
for (auto& c : contentComponents)
|
||||
if (auto comp = c.get())
|
||||
comp->lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void TabbedComponent::changeCallback (int newCurrentTabIndex, const String& newTabName)
|
||||
{
|
||||
auto* newPanelComp = getTabContentComponent (getCurrentTabIndex());
|
||||
|
||||
if (newPanelComp != panelComponent)
|
||||
{
|
||||
if (panelComponent != nullptr)
|
||||
{
|
||||
panelComponent->setVisible (false);
|
||||
removeChildComponent (panelComponent);
|
||||
}
|
||||
|
||||
panelComponent = newPanelComp;
|
||||
|
||||
if (panelComponent != nullptr)
|
||||
{
|
||||
// do these ops as two stages instead of addAndMakeVisible() so that the
|
||||
// component has always got a parent when it gets the visibilityChanged() callback
|
||||
addChildComponent (panelComponent);
|
||||
panelComponent->sendLookAndFeelChange();
|
||||
panelComponent->setVisible (true);
|
||||
panelComponent->toFront (true);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
resized();
|
||||
currentTabChanged (newCurrentTabIndex, newTabName);
|
||||
}
|
||||
|
||||
void TabbedComponent::currentTabChanged (int, const String&) {}
|
||||
void TabbedComponent::popupMenuClickOnTab (int, const String&) {}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> TabbedComponent::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
|
||||
}
|
||||
|
||||
} // namespace juce
|
230
deps/juce/modules/juce_gui_basics/layout/juce_TabbedComponent.h
vendored
Normal file
230
deps/juce/modules/juce_gui_basics/layout/juce_TabbedComponent.h
vendored
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component with a TabbedButtonBar along one of its sides.
|
||||
|
||||
This makes it easy to create a set of tabbed pages, just add a bunch of tabs
|
||||
with addTab(), and this will take care of showing the pages for you when the
|
||||
user clicks on a different tab.
|
||||
|
||||
@see TabbedButtonBar
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API TabbedComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a TabbedComponent, specifying where the tabs should be placed.
|
||||
Once created, add some tabs with the addTab() method.
|
||||
*/
|
||||
explicit TabbedComponent (TabbedButtonBar::Orientation orientation);
|
||||
|
||||
/** Destructor. */
|
||||
~TabbedComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the placement of the tabs.
|
||||
|
||||
This will rearrange the layout to place the tabs along the appropriate
|
||||
side of this component, and will shift the content component accordingly.
|
||||
|
||||
@see TabbedButtonBar::setOrientation
|
||||
*/
|
||||
void setOrientation (TabbedButtonBar::Orientation orientation);
|
||||
|
||||
/** Returns the current tab placement.
|
||||
@see setOrientation, TabbedButtonBar::getOrientation
|
||||
*/
|
||||
TabbedButtonBar::Orientation getOrientation() const noexcept;
|
||||
|
||||
/** Specifies how many pixels wide or high the tab-bar should be.
|
||||
|
||||
If the tabs are placed along the top or bottom, this specified the height
|
||||
of the bar; if they're along the left or right edges, it'll be the width
|
||||
of the bar.
|
||||
*/
|
||||
void setTabBarDepth (int newDepth);
|
||||
|
||||
/** Returns the current thickness of the tab bar.
|
||||
@see setTabBarDepth
|
||||
*/
|
||||
int getTabBarDepth() const noexcept { return tabDepth; }
|
||||
|
||||
/** Specifies the thickness of an outline that should be drawn around the content component.
|
||||
|
||||
If this thickness is > 0, a line will be drawn around the three sides of the content
|
||||
component which don't touch the tab-bar, and the content component will be inset by this amount.
|
||||
|
||||
To set the colour of the line, use setColour (outlineColourId, ...).
|
||||
*/
|
||||
void setOutline (int newThickness);
|
||||
|
||||
/** Specifies a gap to leave around the edge of the content component.
|
||||
Each edge of the content component will be indented by the given number of pixels.
|
||||
*/
|
||||
void setIndent (int indentThickness);
|
||||
|
||||
//==============================================================================
|
||||
/** Removes all the tabs from the bar.
|
||||
@see TabbedButtonBar::clearTabs
|
||||
*/
|
||||
void clearTabs();
|
||||
|
||||
/** Adds a tab to the tab-bar.
|
||||
|
||||
The component passed in will be shown for the tab. If deleteComponentWhenNotNeeded
|
||||
is true, then the TabbedComponent will take ownership of the component and will delete
|
||||
it when the tab is removed or when this object is deleted.
|
||||
|
||||
@see TabbedButtonBar::addTab
|
||||
*/
|
||||
void addTab (const String& tabName,
|
||||
Colour tabBackgroundColour,
|
||||
Component* contentComponent,
|
||||
bool deleteComponentWhenNotNeeded,
|
||||
int insertIndex = -1);
|
||||
|
||||
/** Changes the name of one of the tabs. */
|
||||
void setTabName (int tabIndex, const String& newName);
|
||||
|
||||
/** Gets rid of one of the tabs. */
|
||||
void removeTab (int tabIndex);
|
||||
|
||||
/** Moves a tab to a new index in the list.
|
||||
Pass -1 as the index to move it to the end of the list.
|
||||
*/
|
||||
void moveTab (int currentIndex, int newIndex, bool animate = false);
|
||||
|
||||
/** Returns the number of tabs in the bar. */
|
||||
int getNumTabs() const;
|
||||
|
||||
/** Returns a list of all the tab names in the bar. */
|
||||
StringArray getTabNames() const;
|
||||
|
||||
/** Returns the content component that was added for the given index.
|
||||
Be careful not to reposition or delete the components that are returned, as
|
||||
this will interfere with the TabbedComponent's behaviour.
|
||||
*/
|
||||
Component* getTabContentComponent (int tabIndex) const noexcept;
|
||||
|
||||
/** Returns the colour of one of the tabs. */
|
||||
Colour getTabBackgroundColour (int tabIndex) const noexcept;
|
||||
|
||||
/** Changes the background colour of one of the tabs. */
|
||||
void setTabBackgroundColour (int tabIndex, Colour newColour);
|
||||
|
||||
/** Set deletion policy on removal */
|
||||
void setTabDeleteComponentWhenNotNeeded (int tabIndex, bool flag);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the currently-selected tab.
|
||||
To deselect all the tabs, pass -1 as the index.
|
||||
@see TabbedButtonBar::setCurrentTabIndex
|
||||
*/
|
||||
void setCurrentTabIndex (int newTabIndex, bool sendChangeMessage = true);
|
||||
|
||||
/** Returns the index of the currently selected tab.
|
||||
@see addTab, TabbedButtonBar::getCurrentTabIndex()
|
||||
*/
|
||||
int getCurrentTabIndex() const;
|
||||
|
||||
/** Returns the name of the currently selected tab.
|
||||
@see addTab, TabbedButtonBar::getCurrentTabName()
|
||||
*/
|
||||
String getCurrentTabName() const;
|
||||
|
||||
/** Returns the current component that's filling the panel.
|
||||
This will return nullptr if there isn't one.
|
||||
*/
|
||||
Component* getCurrentContentComponent() const noexcept { return panelComponent.get(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Callback method to indicate the selected tab has been changed.
|
||||
@see setCurrentTabIndex
|
||||
*/
|
||||
virtual void currentTabChanged (int newCurrentTabIndex, const String& newCurrentTabName);
|
||||
|
||||
/** Callback method to indicate that the user has right-clicked on a tab. */
|
||||
virtual void popupMenuClickOnTab (int tabIndex, const String& tabName);
|
||||
|
||||
/** Returns the tab button bar component that is being used. */
|
||||
TabbedButtonBar& getTabbedButtonBar() const noexcept { return *tabs; }
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the component.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1005800, /**< The colour to fill the background behind the tabs. */
|
||||
outlineColourId = 0x1005801, /**< The colour to use to draw an outline around the content.
|
||||
(See setOutline) */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** This creates one of the tab buttons.
|
||||
|
||||
If you need to use custom tab components, you can override this method and
|
||||
return your own class instead of the default.
|
||||
*/
|
||||
virtual TabBarButton* createTabButton (const String& tabName, int tabIndex);
|
||||
|
||||
/** @internal */
|
||||
std::unique_ptr<TabbedButtonBar> tabs;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<WeakReference<Component>> contentComponents;
|
||||
WeakReference<Component> panelComponent;
|
||||
int tabDepth = 30, outlineThickness = 1, edgeIndent = 0;
|
||||
|
||||
struct ButtonBar;
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void changeCallback (int newCurrentTabIndex, const String& newTabName);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
633
deps/juce/modules/juce_gui_basics/layout/juce_Viewport.cpp
vendored
Normal file
633
deps/juce/modules/juce_gui_basics/layout/juce_Viewport.cpp
vendored
Normal file
@ -0,0 +1,633 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
Viewport::Viewport (const String& name) : Component (name)
|
||||
{
|
||||
// content holder is used to clip the contents so they don't overlap the scrollbars
|
||||
addAndMakeVisible (contentHolder);
|
||||
contentHolder.setInterceptsMouseClicks (false, true);
|
||||
|
||||
scrollBarThickness = getLookAndFeel().getDefaultScrollbarWidth();
|
||||
|
||||
setInterceptsMouseClicks (false, true);
|
||||
setWantsKeyboardFocus (true);
|
||||
setScrollOnDragEnabled (Desktop::getInstance().getMainMouseSource().isTouch());
|
||||
|
||||
recreateScrollbars();
|
||||
}
|
||||
|
||||
Viewport::~Viewport()
|
||||
{
|
||||
setScrollOnDragEnabled (false);
|
||||
deleteOrRemoveContentComp();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Viewport::visibleAreaChanged (const Rectangle<int>&) {}
|
||||
void Viewport::viewedComponentChanged (Component*) {}
|
||||
|
||||
//==============================================================================
|
||||
void Viewport::deleteOrRemoveContentComp()
|
||||
{
|
||||
if (contentComp != nullptr)
|
||||
{
|
||||
contentComp->removeComponentListener (this);
|
||||
|
||||
if (deleteContent)
|
||||
{
|
||||
// This sets the content comp to a null pointer before deleting the old one, in case
|
||||
// anything tries to use the old one while it's in mid-deletion..
|
||||
std::unique_ptr<Component> oldCompDeleter (contentComp.get());
|
||||
contentComp = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
contentHolder.removeChildComponent (contentComp);
|
||||
contentComp = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Viewport::setViewedComponent (Component* const newViewedComponent, const bool deleteComponentWhenNoLongerNeeded)
|
||||
{
|
||||
if (contentComp.get() != newViewedComponent)
|
||||
{
|
||||
deleteOrRemoveContentComp();
|
||||
contentComp = newViewedComponent;
|
||||
deleteContent = deleteComponentWhenNoLongerNeeded;
|
||||
|
||||
if (contentComp != nullptr)
|
||||
{
|
||||
contentHolder.addAndMakeVisible (contentComp);
|
||||
setViewPosition (Point<int>());
|
||||
contentComp->addComponentListener (this);
|
||||
}
|
||||
|
||||
viewedComponentChanged (contentComp);
|
||||
updateVisibleArea();
|
||||
}
|
||||
}
|
||||
|
||||
void Viewport::recreateScrollbars()
|
||||
{
|
||||
verticalScrollBar.reset();
|
||||
horizontalScrollBar.reset();
|
||||
|
||||
verticalScrollBar .reset (createScrollBarComponent (true));
|
||||
horizontalScrollBar.reset (createScrollBarComponent (false));
|
||||
|
||||
addChildComponent (verticalScrollBar.get());
|
||||
addChildComponent (horizontalScrollBar.get());
|
||||
|
||||
getVerticalScrollBar().addListener (this);
|
||||
getHorizontalScrollBar().addListener (this);
|
||||
|
||||
resized();
|
||||
}
|
||||
|
||||
int Viewport::getMaximumVisibleWidth() const { return contentHolder.getWidth(); }
|
||||
int Viewport::getMaximumVisibleHeight() const { return contentHolder.getHeight(); }
|
||||
|
||||
bool Viewport::canScrollVertically() const noexcept { return contentComp->getY() < 0 || contentComp->getBottom() > getHeight(); }
|
||||
bool Viewport::canScrollHorizontally() const noexcept { return contentComp->getX() < 0 || contentComp->getRight() > getWidth(); }
|
||||
|
||||
Point<int> Viewport::viewportPosToCompPos (Point<int> pos) const
|
||||
{
|
||||
jassert (contentComp != nullptr);
|
||||
|
||||
auto contentBounds = contentHolder.getLocalArea (contentComp.get(), contentComp->getLocalBounds());
|
||||
|
||||
Point<int> p (jmax (jmin (0, contentHolder.getWidth() - contentBounds.getWidth()), jmin (0, -(pos.x))),
|
||||
jmax (jmin (0, contentHolder.getHeight() - contentBounds.getHeight()), jmin (0, -(pos.y))));
|
||||
|
||||
return p.transformedBy (contentComp->getTransform().inverted());
|
||||
}
|
||||
|
||||
void Viewport::setViewPosition (const int xPixelsOffset, const int yPixelsOffset)
|
||||
{
|
||||
setViewPosition ({ xPixelsOffset, yPixelsOffset });
|
||||
}
|
||||
|
||||
void Viewport::setViewPosition (Point<int> newPosition)
|
||||
{
|
||||
if (contentComp != nullptr)
|
||||
contentComp->setTopLeftPosition (viewportPosToCompPos (newPosition));
|
||||
}
|
||||
|
||||
void Viewport::setViewPositionProportionately (const double x, const double y)
|
||||
{
|
||||
if (contentComp != nullptr)
|
||||
setViewPosition (jmax (0, roundToInt (x * (contentComp->getWidth() - getWidth()))),
|
||||
jmax (0, roundToInt (y * (contentComp->getHeight() - getHeight()))));
|
||||
}
|
||||
|
||||
bool Viewport::autoScroll (const int mouseX, const int mouseY, const int activeBorderThickness, const int maximumSpeed)
|
||||
{
|
||||
if (contentComp != nullptr)
|
||||
{
|
||||
int dx = 0, dy = 0;
|
||||
|
||||
if (getHorizontalScrollBar().isVisible() || canScrollHorizontally())
|
||||
{
|
||||
if (mouseX < activeBorderThickness)
|
||||
dx = activeBorderThickness - mouseX;
|
||||
else if (mouseX >= contentHolder.getWidth() - activeBorderThickness)
|
||||
dx = (contentHolder.getWidth() - activeBorderThickness) - mouseX;
|
||||
|
||||
if (dx < 0)
|
||||
dx = jmax (dx, -maximumSpeed, contentHolder.getWidth() - contentComp->getRight());
|
||||
else
|
||||
dx = jmin (dx, maximumSpeed, -contentComp->getX());
|
||||
}
|
||||
|
||||
if (getVerticalScrollBar().isVisible() || canScrollVertically())
|
||||
{
|
||||
if (mouseY < activeBorderThickness)
|
||||
dy = activeBorderThickness - mouseY;
|
||||
else if (mouseY >= contentHolder.getHeight() - activeBorderThickness)
|
||||
dy = (contentHolder.getHeight() - activeBorderThickness) - mouseY;
|
||||
|
||||
if (dy < 0)
|
||||
dy = jmax (dy, -maximumSpeed, contentHolder.getHeight() - contentComp->getBottom());
|
||||
else
|
||||
dy = jmin (dy, maximumSpeed, -contentComp->getY());
|
||||
}
|
||||
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
contentComp->setTopLeftPosition (contentComp->getX() + dx,
|
||||
contentComp->getY() + dy);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Viewport::componentMovedOrResized (Component&, bool, bool)
|
||||
{
|
||||
updateVisibleArea();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
typedef AnimatedPosition<AnimatedPositionBehaviours::ContinuousWithMomentum> ViewportDragPosition;
|
||||
|
||||
struct Viewport::DragToScrollListener : private MouseListener,
|
||||
private ViewportDragPosition::Listener
|
||||
{
|
||||
DragToScrollListener (Viewport& v) : viewport (v)
|
||||
{
|
||||
viewport.contentHolder.addMouseListener (this, true);
|
||||
offsetX.addListener (this);
|
||||
offsetY.addListener (this);
|
||||
offsetX.behaviour.setMinimumVelocity (60);
|
||||
offsetY.behaviour.setMinimumVelocity (60);
|
||||
}
|
||||
|
||||
~DragToScrollListener() override
|
||||
{
|
||||
viewport.contentHolder.removeMouseListener (this);
|
||||
Desktop::getInstance().removeGlobalMouseListener (this);
|
||||
}
|
||||
|
||||
void positionChanged (ViewportDragPosition&, double) override
|
||||
{
|
||||
viewport.setViewPosition (originalViewPos - Point<int> ((int) offsetX.getPosition(),
|
||||
(int) offsetY.getPosition()));
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e) override
|
||||
{
|
||||
if (! isGlobalMouseListener)
|
||||
{
|
||||
offsetX.setPosition (offsetX.getPosition());
|
||||
offsetY.setPosition (offsetY.getPosition());
|
||||
|
||||
// switch to a global mouse listener so we still receive mouseUp events
|
||||
// if the original event component is deleted
|
||||
viewport.contentHolder.removeMouseListener (this);
|
||||
Desktop::getInstance().addGlobalMouseListener (this);
|
||||
|
||||
isGlobalMouseListener = true;
|
||||
|
||||
scrollSource = e.source;
|
||||
}
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
{
|
||||
if (e.source == scrollSource
|
||||
&& ! doesMouseEventComponentBlockViewportDrag (e.eventComponent))
|
||||
{
|
||||
auto totalOffset = e.getOffsetFromDragStart().toFloat();
|
||||
|
||||
if (! isDragging && totalOffset.getDistanceFromOrigin() > 8.0f)
|
||||
{
|
||||
isDragging = true;
|
||||
|
||||
originalViewPos = viewport.getViewPosition();
|
||||
offsetX.setPosition (0.0);
|
||||
offsetX.beginDrag();
|
||||
offsetY.setPosition (0.0);
|
||||
offsetY.beginDrag();
|
||||
}
|
||||
|
||||
if (isDragging)
|
||||
{
|
||||
offsetX.drag (totalOffset.x);
|
||||
offsetY.drag (totalOffset.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent& e) override
|
||||
{
|
||||
if (isGlobalMouseListener && e.source == scrollSource)
|
||||
endDragAndClearGlobalMouseListener();
|
||||
}
|
||||
|
||||
void endDragAndClearGlobalMouseListener()
|
||||
{
|
||||
offsetX.endDrag();
|
||||
offsetY.endDrag();
|
||||
isDragging = false;
|
||||
|
||||
viewport.contentHolder.addMouseListener (this, true);
|
||||
Desktop::getInstance().removeGlobalMouseListener (this);
|
||||
|
||||
isGlobalMouseListener = false;
|
||||
}
|
||||
|
||||
bool doesMouseEventComponentBlockViewportDrag (const Component* eventComp)
|
||||
{
|
||||
for (auto c = eventComp; c != nullptr && c != &viewport; c = c->getParentComponent())
|
||||
if (c->getViewportIgnoreDragFlag())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Viewport& viewport;
|
||||
ViewportDragPosition offsetX, offsetY;
|
||||
Point<int> originalViewPos;
|
||||
MouseInputSource scrollSource = Desktop::getInstance().getMainMouseSource();
|
||||
bool isDragging = false;
|
||||
bool isGlobalMouseListener = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DragToScrollListener)
|
||||
};
|
||||
|
||||
void Viewport::setScrollOnDragEnabled (bool shouldScrollOnDrag)
|
||||
{
|
||||
if (isScrollOnDragEnabled() != shouldScrollOnDrag)
|
||||
{
|
||||
if (shouldScrollOnDrag)
|
||||
dragToScrollListener.reset (new DragToScrollListener (*this));
|
||||
else
|
||||
dragToScrollListener.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool Viewport::isScrollOnDragEnabled() const noexcept
|
||||
{
|
||||
return dragToScrollListener != nullptr;
|
||||
}
|
||||
|
||||
bool Viewport::isCurrentlyScrollingOnDrag() const noexcept
|
||||
{
|
||||
return dragToScrollListener != nullptr && dragToScrollListener->isDragging;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Viewport::lookAndFeelChanged()
|
||||
{
|
||||
if (! customScrollBarThickness)
|
||||
{
|
||||
scrollBarThickness = getLookAndFeel().getDefaultScrollbarWidth();
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
void Viewport::resized()
|
||||
{
|
||||
updateVisibleArea();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Viewport::updateVisibleArea()
|
||||
{
|
||||
auto scrollbarWidth = getScrollBarThickness();
|
||||
const bool canShowAnyBars = getWidth() > scrollbarWidth && getHeight() > scrollbarWidth;
|
||||
const bool canShowHBar = showHScrollbar && canShowAnyBars;
|
||||
const bool canShowVBar = showVScrollbar && canShowAnyBars;
|
||||
|
||||
bool hBarVisible = false, vBarVisible = false;
|
||||
Rectangle<int> contentArea;
|
||||
|
||||
for (int i = 3; --i >= 0;)
|
||||
{
|
||||
hBarVisible = canShowHBar && ! getHorizontalScrollBar().autoHides();
|
||||
vBarVisible = canShowVBar && ! getVerticalScrollBar().autoHides();
|
||||
contentArea = getLocalBounds();
|
||||
|
||||
if (contentComp != nullptr && ! contentArea.contains (contentComp->getBounds()))
|
||||
{
|
||||
hBarVisible = canShowHBar && (hBarVisible || contentComp->getX() < 0 || contentComp->getRight() > contentArea.getWidth());
|
||||
vBarVisible = canShowVBar && (vBarVisible || contentComp->getY() < 0 || contentComp->getBottom() > contentArea.getHeight());
|
||||
|
||||
if (vBarVisible)
|
||||
contentArea.setWidth (getWidth() - scrollbarWidth);
|
||||
|
||||
if (hBarVisible)
|
||||
contentArea.setHeight (getHeight() - scrollbarWidth);
|
||||
|
||||
if (! contentArea.contains (contentComp->getBounds()))
|
||||
{
|
||||
hBarVisible = canShowHBar && (hBarVisible || contentComp->getRight() > contentArea.getWidth());
|
||||
vBarVisible = canShowVBar && (vBarVisible || contentComp->getBottom() > contentArea.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
if (vBarVisible) contentArea.setWidth (getWidth() - scrollbarWidth);
|
||||
if (hBarVisible) contentArea.setHeight (getHeight() - scrollbarWidth);
|
||||
|
||||
if (! vScrollbarRight && vBarVisible)
|
||||
contentArea.setX (scrollbarWidth);
|
||||
|
||||
if (! hScrollbarBottom && hBarVisible)
|
||||
contentArea.setY (scrollbarWidth);
|
||||
|
||||
if (contentComp == nullptr)
|
||||
{
|
||||
contentHolder.setBounds (contentArea);
|
||||
break;
|
||||
}
|
||||
|
||||
auto oldContentBounds = contentComp->getBounds();
|
||||
contentHolder.setBounds (contentArea);
|
||||
|
||||
// If the content has changed its size, that might affect our scrollbars, so go round again and re-calculate..
|
||||
if (oldContentBounds == contentComp->getBounds())
|
||||
break;
|
||||
}
|
||||
|
||||
Rectangle<int> contentBounds;
|
||||
|
||||
if (auto cc = contentComp.get())
|
||||
contentBounds = contentHolder.getLocalArea (cc, cc->getLocalBounds());
|
||||
|
||||
auto visibleOrigin = -contentBounds.getPosition();
|
||||
|
||||
auto& hbar = getHorizontalScrollBar();
|
||||
auto& vbar = getVerticalScrollBar();
|
||||
|
||||
hbar.setBounds (contentArea.getX(), hScrollbarBottom ? contentArea.getHeight() : 0, contentArea.getWidth(), scrollbarWidth);
|
||||
hbar.setRangeLimits (0.0, contentBounds.getWidth());
|
||||
hbar.setCurrentRange (visibleOrigin.x, contentArea.getWidth());
|
||||
hbar.setSingleStepSize (singleStepX);
|
||||
|
||||
if (canShowHBar && ! hBarVisible)
|
||||
visibleOrigin.setX (0);
|
||||
|
||||
vbar.setBounds (vScrollbarRight ? contentArea.getWidth() : 0, contentArea.getY(), scrollbarWidth, contentArea.getHeight());
|
||||
vbar.setRangeLimits (0.0, contentBounds.getHeight());
|
||||
vbar.setCurrentRange (visibleOrigin.y, contentArea.getHeight());
|
||||
vbar.setSingleStepSize (singleStepY);
|
||||
|
||||
if (canShowVBar && ! vBarVisible)
|
||||
visibleOrigin.setY (0);
|
||||
|
||||
// Force the visibility *after* setting the ranges to avoid flicker caused by edge conditions in the numbers.
|
||||
hbar.setVisible (hBarVisible);
|
||||
vbar.setVisible (vBarVisible);
|
||||
|
||||
if (contentComp != nullptr)
|
||||
{
|
||||
auto newContentCompPos = viewportPosToCompPos (visibleOrigin);
|
||||
|
||||
if (contentComp->getBounds().getPosition() != newContentCompPos)
|
||||
{
|
||||
contentComp->setTopLeftPosition (newContentCompPos); // (this will re-entrantly call updateVisibleArea again)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const Rectangle<int> visibleArea (visibleOrigin.x, visibleOrigin.y,
|
||||
jmin (contentBounds.getWidth() - visibleOrigin.x, contentArea.getWidth()),
|
||||
jmin (contentBounds.getHeight() - visibleOrigin.y, contentArea.getHeight()));
|
||||
|
||||
if (lastVisibleArea != visibleArea)
|
||||
{
|
||||
lastVisibleArea = visibleArea;
|
||||
visibleAreaChanged (visibleArea);
|
||||
}
|
||||
|
||||
hbar.handleUpdateNowIfNeeded();
|
||||
vbar.handleUpdateNowIfNeeded();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Viewport::setSingleStepSizes (const int stepX, const int stepY)
|
||||
{
|
||||
if (singleStepX != stepX || singleStepY != stepY)
|
||||
{
|
||||
singleStepX = stepX;
|
||||
singleStepY = stepY;
|
||||
updateVisibleArea();
|
||||
}
|
||||
}
|
||||
|
||||
void Viewport::setScrollBarsShown (const bool showVerticalScrollbarIfNeeded,
|
||||
const bool showHorizontalScrollbarIfNeeded,
|
||||
const bool allowVerticalScrollingWithoutScrollbar,
|
||||
const bool allowHorizontalScrollingWithoutScrollbar)
|
||||
{
|
||||
allowScrollingWithoutScrollbarV = allowVerticalScrollingWithoutScrollbar;
|
||||
allowScrollingWithoutScrollbarH = allowHorizontalScrollingWithoutScrollbar;
|
||||
|
||||
if (showVScrollbar != showVerticalScrollbarIfNeeded
|
||||
|| showHScrollbar != showHorizontalScrollbarIfNeeded)
|
||||
{
|
||||
showVScrollbar = showVerticalScrollbarIfNeeded;
|
||||
showHScrollbar = showHorizontalScrollbarIfNeeded;
|
||||
updateVisibleArea();
|
||||
}
|
||||
}
|
||||
|
||||
void Viewport::setScrollBarThickness (const int thickness)
|
||||
{
|
||||
int newThickness;
|
||||
|
||||
// To stay compatible with the previous code: use the
|
||||
// default thickness if thickness parameter is zero
|
||||
// or negative
|
||||
if (thickness <= 0)
|
||||
{
|
||||
customScrollBarThickness = false;
|
||||
newThickness = getLookAndFeel().getDefaultScrollbarWidth();
|
||||
}
|
||||
else
|
||||
{
|
||||
customScrollBarThickness = true;
|
||||
newThickness = thickness;
|
||||
}
|
||||
|
||||
if (scrollBarThickness != newThickness)
|
||||
{
|
||||
scrollBarThickness = newThickness;
|
||||
updateVisibleArea();
|
||||
}
|
||||
}
|
||||
|
||||
int Viewport::getScrollBarThickness() const
|
||||
{
|
||||
return scrollBarThickness;
|
||||
}
|
||||
|
||||
void Viewport::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart)
|
||||
{
|
||||
auto newRangeStartInt = roundToInt (newRangeStart);
|
||||
|
||||
if (scrollBarThatHasMoved == horizontalScrollBar.get())
|
||||
{
|
||||
setViewPosition (newRangeStartInt, getViewPositionY());
|
||||
}
|
||||
else if (scrollBarThatHasMoved == verticalScrollBar.get())
|
||||
{
|
||||
setViewPosition (getViewPositionX(), newRangeStartInt);
|
||||
}
|
||||
}
|
||||
|
||||
void Viewport::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
|
||||
{
|
||||
if (! useMouseWheelMoveIfNeeded (e, wheel))
|
||||
Component::mouseWheelMove (e, wheel);
|
||||
}
|
||||
|
||||
static int rescaleMouseWheelDistance (float distance, int singleStepSize) noexcept
|
||||
{
|
||||
if (distance == 0.0f)
|
||||
return 0;
|
||||
|
||||
distance *= 14.0f * (float) singleStepSize;
|
||||
|
||||
return roundToInt (distance < 0 ? jmin (distance, -1.0f)
|
||||
: jmax (distance, 1.0f));
|
||||
}
|
||||
|
||||
bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, const MouseWheelDetails& wheel)
|
||||
{
|
||||
if (! (e.mods.isAltDown() || e.mods.isCtrlDown() || e.mods.isCommandDown()))
|
||||
{
|
||||
const bool canScrollVert = (allowScrollingWithoutScrollbarV || getVerticalScrollBar().isVisible());
|
||||
const bool canScrollHorz = (allowScrollingWithoutScrollbarH || getHorizontalScrollBar().isVisible());
|
||||
|
||||
if (canScrollHorz || canScrollVert)
|
||||
{
|
||||
auto deltaX = rescaleMouseWheelDistance (wheel.deltaX, singleStepX);
|
||||
auto deltaY = rescaleMouseWheelDistance (wheel.deltaY, singleStepY);
|
||||
|
||||
auto pos = getViewPosition();
|
||||
|
||||
if (deltaX != 0 && deltaY != 0 && canScrollHorz && canScrollVert)
|
||||
{
|
||||
pos.x -= deltaX;
|
||||
pos.y -= deltaY;
|
||||
}
|
||||
else if (canScrollHorz && (deltaX != 0 || e.mods.isShiftDown() || ! canScrollVert))
|
||||
{
|
||||
pos.x -= deltaX != 0 ? deltaX : deltaY;
|
||||
}
|
||||
else if (canScrollVert && deltaY != 0)
|
||||
{
|
||||
pos.y -= deltaY;
|
||||
}
|
||||
|
||||
if (pos != getViewPosition())
|
||||
{
|
||||
setViewPosition (pos);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isUpDownKeyPress (const KeyPress& key)
|
||||
{
|
||||
return key == KeyPress::upKey
|
||||
|| key == KeyPress::downKey
|
||||
|| key == KeyPress::pageUpKey
|
||||
|| key == KeyPress::pageDownKey
|
||||
|| key == KeyPress::homeKey
|
||||
|| key == KeyPress::endKey;
|
||||
}
|
||||
|
||||
static bool isLeftRightKeyPress (const KeyPress& key)
|
||||
{
|
||||
return key == KeyPress::leftKey
|
||||
|| key == KeyPress::rightKey;
|
||||
}
|
||||
|
||||
bool Viewport::keyPressed (const KeyPress& key)
|
||||
{
|
||||
const bool isUpDownKey = isUpDownKeyPress (key);
|
||||
|
||||
if (getVerticalScrollBar().isVisible() && isUpDownKey)
|
||||
return getVerticalScrollBar().keyPressed (key);
|
||||
|
||||
const bool isLeftRightKey = isLeftRightKeyPress (key);
|
||||
|
||||
if (getHorizontalScrollBar().isVisible() && (isUpDownKey || isLeftRightKey))
|
||||
return getHorizontalScrollBar().keyPressed (key);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Viewport::respondsToKey (const KeyPress& key)
|
||||
{
|
||||
return isUpDownKeyPress (key) || isLeftRightKeyPress (key);
|
||||
}
|
||||
|
||||
ScrollBar* Viewport::createScrollBarComponent (bool isVertical)
|
||||
{
|
||||
return new ScrollBar (isVertical);
|
||||
}
|
||||
|
||||
void Viewport::setScrollBarPosition (bool verticalScrollbarOnRight,
|
||||
bool horizontalScrollbarAtBottom)
|
||||
{
|
||||
vScrollbarRight = verticalScrollbarOnRight;
|
||||
hScrollbarBottom = horizontalScrollbarAtBottom;
|
||||
|
||||
resized();
|
||||
}
|
||||
|
||||
} // namespace juce
|
339
deps/juce/modules/juce_gui_basics/layout/juce_Viewport.h
vendored
Normal file
339
deps/juce/modules/juce_gui_basics/layout/juce_Viewport.h
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Viewport is used to contain a larger child component, and allows the child
|
||||
to be automatically scrolled around.
|
||||
|
||||
To use a Viewport, just create one and set the component that goes inside it
|
||||
using the setViewedComponent() method. When the child component changes size,
|
||||
the Viewport will adjust its scrollbars accordingly.
|
||||
|
||||
A subclass of the viewport can be created which will receive calls to its
|
||||
visibleAreaChanged() method when the subcomponent changes position or size.
|
||||
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API Viewport : public Component,
|
||||
private ComponentListener,
|
||||
private ScrollBar::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a Viewport.
|
||||
|
||||
The viewport is initially empty - use the setViewedComponent() method to
|
||||
add a child component for it to manage.
|
||||
*/
|
||||
explicit Viewport (const String& componentName = String());
|
||||
|
||||
/** Destructor. */
|
||||
~Viewport() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the component that this viewport will contain and scroll around.
|
||||
|
||||
This will add the given component to this Viewport and position it at (0, 0).
|
||||
|
||||
(Don't add or remove any child components directly using the normal
|
||||
Component::addChildComponent() methods).
|
||||
|
||||
@param newViewedComponent the component to add to this viewport, or null to remove
|
||||
the current component.
|
||||
@param deleteComponentWhenNoLongerNeeded if true, the component will be deleted
|
||||
automatically when the viewport is deleted or when a different
|
||||
component is added. If false, the caller must manage the lifetime
|
||||
of the component
|
||||
@see getViewedComponent
|
||||
*/
|
||||
void setViewedComponent (Component* newViewedComponent,
|
||||
bool deleteComponentWhenNoLongerNeeded = true);
|
||||
|
||||
/** Returns the component that's currently being used inside the Viewport.
|
||||
|
||||
@see setViewedComponent
|
||||
*/
|
||||
Component* getViewedComponent() const noexcept { return contentComp.get(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the position of the viewed component.
|
||||
|
||||
The inner component will be moved so that the pixel at the top left of
|
||||
the viewport will be the pixel at position (xPixelsOffset, yPixelsOffset)
|
||||
within the inner component.
|
||||
|
||||
This will update the scrollbars and might cause a call to visibleAreaChanged().
|
||||
|
||||
@see getViewPositionX, getViewPositionY, setViewPositionProportionately
|
||||
*/
|
||||
void setViewPosition (int xPixelsOffset, int yPixelsOffset);
|
||||
|
||||
/** Changes the position of the viewed component.
|
||||
|
||||
The inner component will be moved so that the pixel at the top left of
|
||||
the viewport will be the pixel at the specified coordinates within the
|
||||
inner component.
|
||||
|
||||
This will update the scrollbars and might cause a call to visibleAreaChanged().
|
||||
|
||||
@see getViewPositionX, getViewPositionY, setViewPositionProportionately
|
||||
*/
|
||||
void setViewPosition (Point<int> newPosition);
|
||||
|
||||
/** Changes the view position as a proportion of the distance it can move.
|
||||
|
||||
The values here are from 0.0 to 1.0 - where (0, 0) would put the
|
||||
visible area in the top-left, and (1, 1) would put it as far down and
|
||||
to the right as it's possible to go whilst keeping the child component
|
||||
on-screen.
|
||||
*/
|
||||
void setViewPositionProportionately (double proportionX, double proportionY);
|
||||
|
||||
/** If the specified position is at the edges of the viewport, this method scrolls
|
||||
the viewport to bring that position nearer to the centre.
|
||||
|
||||
Call this if you're dragging an object inside a viewport and want to make it scroll
|
||||
when the user approaches an edge. You might also find Component::beginDragAutoRepeat()
|
||||
useful when auto-scrolling.
|
||||
|
||||
@param mouseX the x position, relative to the Viewport's top-left
|
||||
@param mouseY the y position, relative to the Viewport's top-left
|
||||
@param distanceFromEdge specifies how close to an edge the position needs to be
|
||||
before the viewport should scroll in that direction
|
||||
@param maximumSpeed the maximum number of pixels that the viewport is allowed
|
||||
to scroll by.
|
||||
@returns true if the viewport was scrolled
|
||||
*/
|
||||
bool autoScroll (int mouseX, int mouseY, int distanceFromEdge, int maximumSpeed);
|
||||
|
||||
/** Returns the position within the child component of the top-left of its visible area. */
|
||||
Point<int> getViewPosition() const noexcept { return lastVisibleArea.getPosition(); }
|
||||
|
||||
/** Returns the visible area of the child component, relative to its top-left */
|
||||
Rectangle<int> getViewArea() const noexcept { return lastVisibleArea; }
|
||||
|
||||
/** Returns the position within the child component of the top-left of its visible area.
|
||||
@see getViewWidth, setViewPosition
|
||||
*/
|
||||
int getViewPositionX() const noexcept { return lastVisibleArea.getX(); }
|
||||
|
||||
/** Returns the position within the child component of the top-left of its visible area.
|
||||
@see getViewHeight, setViewPosition
|
||||
*/
|
||||
int getViewPositionY() const noexcept { return lastVisibleArea.getY(); }
|
||||
|
||||
/** Returns the width of the visible area of the child component.
|
||||
|
||||
This may be less than the width of this Viewport if there's a vertical scrollbar
|
||||
or if the child component is itself smaller.
|
||||
*/
|
||||
int getViewWidth() const noexcept { return lastVisibleArea.getWidth(); }
|
||||
|
||||
/** Returns the height of the visible area of the child component.
|
||||
|
||||
This may be less than the height of this Viewport if there's a horizontal scrollbar
|
||||
or if the child component is itself smaller.
|
||||
*/
|
||||
int getViewHeight() const noexcept { return lastVisibleArea.getHeight(); }
|
||||
|
||||
/** Returns the width available within this component for the contents.
|
||||
|
||||
This will be the width of the viewport component minus the width of a
|
||||
vertical scrollbar (if visible).
|
||||
*/
|
||||
int getMaximumVisibleWidth() const;
|
||||
|
||||
/** Returns the height available within this component for the contents.
|
||||
|
||||
This will be the height of the viewport component minus the space taken up
|
||||
by a horizontal scrollbar (if visible).
|
||||
*/
|
||||
int getMaximumVisibleHeight() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Callback method that is called when the visible area changes.
|
||||
|
||||
This will be called when the visible area is moved either be scrolling or
|
||||
by calls to setViewPosition(), etc.
|
||||
*/
|
||||
virtual void visibleAreaChanged (const Rectangle<int>& newVisibleArea);
|
||||
|
||||
/** Callback method that is called when the viewed component is added, removed or swapped. */
|
||||
virtual void viewedComponentChanged (Component* newComponent);
|
||||
|
||||
//==============================================================================
|
||||
/** Turns scrollbars on or off.
|
||||
|
||||
If set to false, the scrollbars won't ever appear. When true (the default)
|
||||
they will appear only when needed.
|
||||
|
||||
The allowVerticalScrollingWithoutScrollbar parameters allow you to enable
|
||||
mouse-wheel scrolling even when there the scrollbars are hidden. When the
|
||||
scrollbars are visible, these parameters are ignored.
|
||||
*/
|
||||
void setScrollBarsShown (bool showVerticalScrollbarIfNeeded,
|
||||
bool showHorizontalScrollbarIfNeeded,
|
||||
bool allowVerticalScrollingWithoutScrollbar = false,
|
||||
bool allowHorizontalScrollingWithoutScrollbar = false);
|
||||
|
||||
/** Changes where the scroll bars are positioned
|
||||
|
||||
If verticalScrollbarOnRight is set to true, then the vertical scrollbar will
|
||||
appear on the right side of the view port's content (this is the default),
|
||||
otherwise it will be on the left side of the content.
|
||||
|
||||
If horizontalScrollbarAtBottom is set to true, then the horizontal scrollbar
|
||||
will appear at the bottom of the view port's content (this is the default),
|
||||
otherwise it will be at the top.
|
||||
*/
|
||||
void setScrollBarPosition (bool verticalScrollbarOnRight,
|
||||
bool horizontalScrollbarAtBottom);
|
||||
|
||||
/** True if the vertical scrollbar will appear on the right side of the content */
|
||||
bool isVerticalScrollbarOnTheRight() const noexcept { return vScrollbarRight; }
|
||||
|
||||
/** True if the horizontal scrollbar will appear at the bottom of the content */
|
||||
bool isHorizontalScrollbarAtBottom() const noexcept { return hScrollbarBottom; }
|
||||
|
||||
/** True if the vertical scrollbar is enabled.
|
||||
@see setScrollBarsShown
|
||||
*/
|
||||
bool isVerticalScrollBarShown() const noexcept { return showVScrollbar; }
|
||||
|
||||
/** True if the horizontal scrollbar is enabled.
|
||||
@see setScrollBarsShown
|
||||
*/
|
||||
bool isHorizontalScrollBarShown() const noexcept { return showHScrollbar; }
|
||||
|
||||
/** Changes the width of the scrollbars.
|
||||
If this isn't specified, the default width from the LookAndFeel class will be used.
|
||||
@see LookAndFeel::getDefaultScrollbarWidth
|
||||
*/
|
||||
void setScrollBarThickness (int thickness);
|
||||
|
||||
/** Returns the thickness of the scrollbars.
|
||||
@see setScrollBarThickness
|
||||
*/
|
||||
int getScrollBarThickness() const;
|
||||
|
||||
/** Changes the distance that a single-step click on a scrollbar button
|
||||
will move the viewport.
|
||||
*/
|
||||
void setSingleStepSizes (int stepX, int stepY);
|
||||
|
||||
/** Returns a reference to the scrollbar component being used.
|
||||
Handy if you need to customise the bar somehow.
|
||||
*/
|
||||
ScrollBar& getVerticalScrollBar() noexcept { return *verticalScrollBar; }
|
||||
|
||||
/** Returns a reference to the scrollbar component being used.
|
||||
Handy if you need to customise the bar somehow.
|
||||
*/
|
||||
ScrollBar& getHorizontalScrollBar() noexcept { return *horizontalScrollBar; }
|
||||
|
||||
/** Re-instantiates the scrollbars, which is only really useful if you've overridden createScrollBarComponent(). */
|
||||
void recreateScrollbars();
|
||||
|
||||
/** True if there's any off-screen content that could be scrolled vertically,
|
||||
or false if everything is currently visible.
|
||||
*/
|
||||
bool canScrollVertically() const noexcept;
|
||||
|
||||
/** True if there's any off-screen content that could be scrolled horizontally,
|
||||
or false if everything is currently visible.
|
||||
*/
|
||||
bool canScrollHorizontally() const noexcept;
|
||||
|
||||
/** Enables or disables drag-to-scroll functionality in the viewport.
|
||||
|
||||
If your viewport contains a Component that you don't want to receive mouse events when the
|
||||
user is drag-scrolling, you can disable this with the Component::setViewportIgnoreDragFlag()
|
||||
method.
|
||||
*/
|
||||
void setScrollOnDragEnabled (bool shouldScrollOnDrag);
|
||||
|
||||
/** Returns true if drag-to-scroll functionality is enabled. */
|
||||
bool isScrollOnDragEnabled() const noexcept;
|
||||
|
||||
/** Returns true if the user is currently dragging-to-scroll.
|
||||
@see setScrollOnDragEnabled
|
||||
*/
|
||||
bool isCurrentlyScrollingOnDrag() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void scrollBarMoved (ScrollBar*, double newRangeStart) override;
|
||||
/** @internal */
|
||||
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
bool useMouseWheelMoveIfNeeded (const MouseEvent&, const MouseWheelDetails&);
|
||||
/** @internal */
|
||||
static bool respondsToKey (const KeyPress&);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates the Scrollbar components that will be added to the Viewport.
|
||||
Subclasses can override this if they need to customise the scrollbars in some way.
|
||||
*/
|
||||
virtual ScrollBar* createScrollBarComponent (bool isVertical);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<ScrollBar> verticalScrollBar, horizontalScrollBar;
|
||||
Component contentHolder;
|
||||
WeakReference<Component> contentComp;
|
||||
Rectangle<int> lastVisibleArea;
|
||||
int scrollBarThickness = 0;
|
||||
int singleStepX = 16, singleStepY = 16;
|
||||
bool showHScrollbar = true, showVScrollbar = true, deleteContent = true;
|
||||
bool customScrollBarThickness = false;
|
||||
bool allowScrollingWithoutScrollbarV = false, allowScrollingWithoutScrollbarH = false;
|
||||
bool vScrollbarRight = true, hScrollbarBottom = true;
|
||||
|
||||
struct DragToScrollListener;
|
||||
std::unique_ptr<DragToScrollListener> dragToScrollListener;
|
||||
|
||||
Point<int> viewportPosToCompPos (Point<int>) const;
|
||||
|
||||
void updateVisibleArea();
|
||||
void deleteOrRemoveContentComp();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Viewport)
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user