/* ============================================================================== Split component for Juce File: SplitComponent.cpp Created: 15 Mar 2014 -------------------------------------------------------------------------------- Copyright 2014 Jim Hewes Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ============================================================================== */ #include #include "SplitComponent.h" SplitComponent::SplitComponent(bool isVertical) : m_pTopLeftComponent(NULL) , m_pBottomRightComponent(NULL) , m_isVertical(isVertical) , m_splitBarGravity(0.0) , m_fitToParent(true) { CommonConstruction(); } SplitComponent::SplitComponent(Component& topLeftComponent, Component& bottomRightComponent, bool isVertical) : m_pTopLeftComponent(&topLeftComponent) , m_pBottomRightComponent(&bottomRightComponent) , m_isVertical(isVertical) , m_splitBarGravity(0.0) , m_fitToParent(true) { CommonConstruction(); } void SplitComponent::CommonConstruction() { // Set the bounds of this split component based on the parent's size (if there is a parent). parentSizeChanged(); m_pSplitBar = new SplitBar(*this, m_isVertical); // set default position and width of the SplitBar if (m_isVertical) { m_pSplitBar->setBounds(getWidth() / 2, 0, 8, getHeight()); m_lastSplitbarMovePosition = m_pSplitBar->getX(); m_lastSplitbarMoveExtent = getWidth(); } else { m_pSplitBar->setBounds(0, getHeight() / 2, getWidth(), 8); m_lastSplitbarMovePosition = m_pSplitBar->getY(); m_lastSplitbarMoveExtent = getHeight(); } if (m_pTopLeftComponent) { addAndMakeVisible(m_pTopLeftComponent, 0); } if (m_pBottomRightComponent) { addAndMakeVisible(m_pBottomRightComponent, 1); } addAndMakeVisible(m_pSplitBar, -1); // make sure the split bar is in front of other components. } SplitComponent::~SplitComponent() { removeAllChildren(); } void SplitComponent::SetTopLeftComponent(Component &component) { if (m_pTopLeftComponent) { removeChildComponent(m_pTopLeftComponent); } m_pTopLeftComponent = &component; addAndMakeVisible(m_pTopLeftComponent, 0); Layout(); } void SplitComponent::SetBottomRightComponent(Component &component) { if (m_pBottomRightComponent) { removeChildComponent(m_pBottomRightComponent); } m_pBottomRightComponent = &component; addAndMakeVisible(m_pBottomRightComponent, 1); Layout(); } void SplitComponent::Layout() { Rectangle topLeftComponentRect; Rectangle bottomRightComponentRect; topLeftComponentRect.setTop(0); topLeftComponentRect.setLeft(0); if (m_isVertical) { int splitBarPosX = m_pSplitBar->getX(); int splitBarWidth = m_pSplitBar->getWidth(); topLeftComponentRect.setWidth(splitBarPosX); topLeftComponentRect.setHeight(getHeight()); bottomRightComponentRect.setPosition(splitBarPosX + splitBarWidth, 0); bottomRightComponentRect.setWidth(getWidth() - (splitBarPosX + splitBarWidth)); bottomRightComponentRect.setHeight(getHeight()); } else { int splitBarPosY = m_pSplitBar->getY(); int splitBarHeight = m_pSplitBar->getHeight(); topLeftComponentRect.setWidth(getWidth()); topLeftComponentRect.setHeight(splitBarPosY); bottomRightComponentRect.setPosition(0, splitBarPosY + splitBarHeight); bottomRightComponentRect.setWidth(getWidth()); bottomRightComponentRect.setHeight(getHeight() - (splitBarPosY + splitBarHeight)); } if (m_pTopLeftComponent) { m_pTopLeftComponent->setBounds(topLeftComponentRect); } if (m_pBottomRightComponent) { m_pBottomRightComponent->setBounds(bottomRightComponentRect); } } void SplitComponent::SetSplitBarPosition(int newPosition) { if (m_isVertical) { newPosition = jmax(0, jmin(newPosition, getWidth() - m_pSplitBar->getWidth())); // limit m_pSplitBar->setBounds(newPosition, 0, m_pSplitBar->getWidth(), m_pSplitBar->getHeight()); m_lastSplitbarMoveExtent = getWidth(); } else { newPosition = jmax(0, jmin(newPosition, getHeight() - m_pSplitBar->getHeight())); // limit m_pSplitBar->setBounds(0, newPosition, m_pSplitBar->getWidth(), m_pSplitBar->getHeight()); m_lastSplitbarMoveExtent = getHeight(); } m_lastSplitbarMovePosition = newPosition; Layout(); } int SplitComponent::GetSplitBarPosition() const { return m_isVertical ? m_pSplitBar->getX() : m_pSplitBar->getY(); } void SplitComponent::SetSplitBarThickness(int newThickness) { // The position of the split bar may now be invalid as a result of the new thickness, so adjust. int splitBarPosition; if (m_isVertical) { newThickness = jmax(0, jmin(newThickness, getWidth() - 2)); splitBarPosition = jmin(m_pSplitBar->getX(), getWidth() - newThickness); // limit split bar position m_pSplitBar->setBounds(splitBarPosition, 0, newThickness, m_pSplitBar->getHeight()); } else { newThickness = jmax(0, jmin(newThickness, getHeight() - 2)); splitBarPosition = jmin(m_pSplitBar->getY(), getHeight() - newThickness); m_pSplitBar->setBounds(0, splitBarPosition, m_pSplitBar->getWidth(), newThickness); } SetSplitBarPosition(splitBarPosition); } int SplitComponent::GetSplitBarThickness() const { return m_isVertical ? m_pSplitBar->getWidth() : m_pSplitBar->getHeight(); } void SplitComponent::SetSplitBarGravity(double gravity) noexcept { assert((gravity >= 0.0) && (gravity <= 1.0)); // limit m_splitBarGravity = (gravity < 0.0) ? 0.0 : (gravity > 1.0) ? 1.0 : gravity; } double SplitComponent::GetSplitBarGravity() const noexcept { return m_splitBarGravity;; } void SplitComponent::SetVerticalSplit(bool isVertical) { if (m_isVertical != isVertical) { // Change the orientation of the split bar. // The split bar position might now be invalid (outside the SplitComponent bounds). So move it to be valid. int splitbarThickness; int splitBarPosition; if (m_isVertical) // change from vertical to horizontal { splitbarThickness = m_pSplitBar->getWidth(); splitBarPosition = m_pSplitBar->getX(); // limit split bar position splitBarPosition = jmin(splitBarPosition, getHeight() - splitbarThickness); m_pSplitBar->setBounds(0, splitBarPosition, getWidth(), splitbarThickness); } else // change from horizontal to vertical { splitbarThickness = m_pSplitBar->getHeight(); splitBarPosition = m_pSplitBar->getY(); // limit split bar position splitBarPosition = jmin(splitBarPosition, getWidth() - splitbarThickness); m_pSplitBar->setBounds(splitBarPosition, 0, splitbarThickness, getHeight()); } m_pSplitBar->SetVertical(isVertical); m_isVertical = isVertical; SetSplitBarPosition(splitBarPosition); } } bool SplitComponent::IsVerticalSplit() const { return m_isVertical; } void SplitComponent::SetFitToParent(bool fitToParent) { if (m_fitToParent != fitToParent) { m_fitToParent = fitToParent; parentSizeChanged(); } } bool SplitComponent::GetFitToParent() const { return m_fitToParent; } void SplitComponent::resized() { if (m_isVertical) { // Account for gravity int widthDiff = getWidth() - m_lastSplitbarMoveExtent; // Get the difference in SplitComponent width since the split bar was moved manually. widthDiff = int(widthDiff * m_splitBarGravity); // Get a percentage of that difference based on the gravity setting. int newXPosition = widthDiff + m_lastSplitbarMovePosition; // Add the difference to getthe new desired right position. (which may get limited below) // Keep the split bar inside the visible area int maxSplitPosition = getWidth() - m_pSplitBar->getWidth(); newXPosition = juce::jmax(0, juce::jmin(maxSplitPosition, newXPosition)); // Essentially, adjust the height of the split bar to be the height of the SplitComponent. m_pSplitBar->setBounds(newXPosition, 0, m_pSplitBar->getWidth(), getHeight()); } else { // Account for gravity int heightDiff = getHeight() - m_lastSplitbarMoveExtent; // Get the difference in SplitComponent width since the split bar was moved manually. heightDiff = int(heightDiff * m_splitBarGravity); // Get a percentage of that difference based on the gravity setting. int newYPosition = heightDiff + m_lastSplitbarMovePosition; // Add the difference to getthe new desired right position. (which may get limited below) // Keep the split bar inside the visible area newYPosition = juce::jlimit(0, getHeight() - m_pSplitBar->getHeight(), newYPosition); // Essentially, adjust the width of the split bar to be the width of the SplitComponent. m_pSplitBar->setBounds(0, newYPosition, getWidth(), m_pSplitBar->getHeight()); } Layout(); } /** Resize the SplitComponent to fills the area of it's parent if fit-to-parent is set. */ void SplitComponent::parentSizeChanged() { Component* pParentComponent = getParentComponent(); if (pParentComponent && m_fitToParent) { // Occupy the entire space of the parent component. // Use Rectangle::setPosition(0,0) and NOT Rectangle::setLeft(0) Rectangle::setTop(0). // The later two would change the width and height of the rectangle so that the right and // bottom edges stay in the same place. setPosition does not have that side effect. Rectangle bnds= pParentComponent->getBounds(); bnds.setPosition(0, 0); setBounds(bnds); // This will cause SplitComponent::resized() to be called. } } void SplitComponent::parentHierarchyChanged() { // If the parent changed, we no longer know if the split bar position is within the parent's bounds. // So treat it as if the parent size changed. This also takes care of the case where a SplitComponent is added // to a parent for the first time and the split position was not initialized. parentSizeChanged(); } ////////////////////////////////////////////////////////////////////////////////////////////////// // // Splitbar // // SplitComponent::SplitBar::SplitBar(SplitComponent& splitComponent, bool isVertical) : m_isVertical(isVertical) , m_splitComponent(splitComponent) { setRepaintsOnMouseActivity(true); setMouseCursor(isVertical ? MouseCursor::LeftRightResizeCursor : MouseCursor::UpDownResizeCursor); } void SplitComponent::SplitBar::paint(Graphics& g) { getLookAndFeel().drawStretchableLayoutResizerBar(g, getWidth(), getHeight(), m_isVertical, isMouseOver(), isMouseButtonDown()); } void SplitComponent::SplitBar::mouseDown(const MouseEvent&) { const Point pos = getPosition(); m_mouseDownPos = m_isVertical ? pos.getX() : pos.getY(); } void SplitComponent::SplitBar::mouseDrag(const MouseEvent& e) { int desiredPos = m_mouseDownPos + (m_isVertical ? e.getDistanceFromDragStartX() : e.getDistanceFromDragStartY()); // Adjust position to keep the split bar inside the SplitComponent bounds. desiredPos = jmax(desiredPos, 0); if (m_isVertical) { desiredPos = jmin(desiredPos, m_splitComponent.getWidth() - getWidth()); } else { desiredPos = jmin(desiredPos, m_splitComponent.getHeight() - getHeight()); } if (desiredPos != m_mouseDownPos) { m_splitComponent.SetSplitBarPosition(desiredPos); } } void SplitComponent::SplitBar::SetVertical(bool isVertical) { if (m_isVertical != isVertical) { m_isVertical = isVertical; setMouseCursor(m_isVertical ? MouseCursor::LeftRightResizeCursor : MouseCursor::UpDownResizeCursor); } } bool SplitComponent::SplitBar::IsVertical() { return m_isVertical; }