diff options
Diffstat (limited to 'src/Authoring/Qt3DStudio/Controls/Control.cpp')
-rw-r--r-- | src/Authoring/Qt3DStudio/Controls/Control.cpp | 1738 |
1 files changed, 1738 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Controls/Control.cpp b/src/Authoring/Qt3DStudio/Controls/Control.cpp new file mode 100644 index 00000000..03f01403 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/Control.cpp @@ -0,0 +1,1738 @@ +/**************************************************************************** +** +** Copyright (C) 2002 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "Qt3DSCommonPrecompile.h" + +#include "Control.h" +#include "Renderer.h" +#include "MasterP.h" +#include "StudioUtils.h" +#include "HotKeys.h" +#include "ControlGraph.h" +#include "ControlData.h" +#include "ResourceCache.h" +#include "MouseCursor.h" + +#include <QtWidgets/qapplication.h> + +using namespace Q3DStudio::Control; + +IMPLEMENT_OBJECT_COUNTER(CControl) + +//============================================================================= +/** + * Constructor, creates a control with all default values. + */ +CControl::CControl() + : m_cursorSet(-1) +{ + m_ControlData = CControlData::CreateControlData(*this); + ADDTO_OBJECT_COUNTER(CControl) +} + +//============================================================================= +/** + * Destructor + */ +CControl::~CControl() +{ + REMOVEFROM_OBJECT_COUNTER(CControl) + m_ControlData->ReleaseControl(); +} + +//============================================================================= +/** + * Draw this control and all children below it. + * This can be overriden by controls to draw themselves but should still be + * called if sub controls are to be drawn. + * This will call Draw if this control is actually supposed to be drawn. It will + * be drawn if it is invalidated or if anything above it in it's heirarchy has + * been invalidated. + * @param inRenderer the renderer to draw this control out to. + */ +void CControl::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, + bool inIgnoreValidation /* = false */) +{ + EnsureLayout(); + bool isInvalidated = IsInvalidated(); + + // inRenderer->PushClippingRect( GetSize( ) ); + + CRct theBoundingBox = inRenderer->GetClippingRect(); + + if (isInvalidated) { + CRct theRect(GetSize()); + theRect.And(theBoundingBox); + theRect.Offset(inRenderer->GetTranslation()); + inDirtyRect.Or(theRect); + } + + if (isInvalidated || inIgnoreValidation) + Draw(inRenderer); + + // Notify the children in the reverse order that they are drawn. + ControlGraph::SReverseIterator theRPos = ControlGraph::GetRChildren(*this); + for (; !theRPos.IsDone(); ++theRPos) { + (*theRPos)->EnsureLayout(); + (*theRPos)->BeginDrawChildren(inRenderer); + } + + // Go through all the children and draw them in the correct order. By keeping + // this order there is a semblance of depth. + ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this); + for (; !thePos.IsDone(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + if (theChild->IsVisible()) { + CPt thePosition = theChild->GetPosition(); + inRenderer->PushTranslation(thePosition); + + // Clipping of non-visible objects. + if (theChild->IsInRect(theBoundingBox)) + theChild->OnDraw(inRenderer, inDirtyRect, inIgnoreValidation || isInvalidated); + else + theChild->NotifyNotInClipRect(); + inRenderer->PopTranslation(); + } + } + + // inRenderer->PopClippingRect( ); + + // Set this as not being invalidated. + Invalidate(false); +} + +//============================================================================= +/** + * Performs the drawing for this control. + * Does nothing by default. + * @param inRenderer the renderer to draw to. + */ +void CControl::Draw(CRenderer *inRenderer) +{ + Q_UNUSED(inRenderer); +} + +//============================================================================= +/** + * Notification that this control is not in the clipping rect and hence will + * not be drawn. + * By default, tell inform its children + */ +void CControl::NotifyNotInClipRect() +{ + ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this); + for (; !thePos.IsDone(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + theChild->NotifyNotInClipRect(); + } +} + +//============================================================================= +/** + * Get the position of this control. + * The position of this control is relative to it's parent's 0,0 coordinate. + * @return the position relative to it's parent's position. + */ +CPt CControl::GetPosition() const +{ + return m_ControlData->m_Position; +} + +//============================================================================= +/** + * Set the position of this control. + * The position of this control is relative to it's parent's 0,0 coordinate. + * @return the position relative to it's parent's position. + */ +void CControl::SetPosition(CPt inPosition) +{ + if (inPosition != m_ControlData->m_Position) { + MarkChildrenNeedLayout(); + m_ControlData->m_Position = inPosition; + Invalidate(); + } +} + +//============================================================================= +/** + * Overload of SetPosition to allow it to take 2 parameters. + * This will call SetPosition( CPt ) so there is no need to extend this + * method. + * @param inX the X position to set. + * @param inY the Y position to set. + */ +void CControl::SetPosition(long inX, long inY) +{ + SetPosition(CPt(inX, inY)); +} + +//============================================================================= +/** + * Get the size of the control. + * The size is the width and height of the control from it's position. + * @return the size of this control. + */ +CPt CControl::GetSize() const +{ + return m_ControlData->m_Size; +} + +//============================================================================= +/** + * Set the size of this control. + * The size is the width and height of the control from it's position. + * @param inSize the new size of the control. + */ +void CControl::SetSize(CPt inSize) +{ + if (GetSize() != inSize) { + m_ControlData->m_Size = inSize; + if (GetParent() != nullptr) + GetParent()->OnChildSizeChanged(this); // this callback needs to die + OnSizeChanged(inSize); + } +} + +void CControl::OnSizeChanged(CPt /*inSize*/) +{ + MarkChildrenNeedLayout(); + Invalidate(); +} + +//============================================================================= +/** + * Overload of SetSize to allow it to take 2 parameters. + * This will call SetSize( CPt ) so there is no need to extend this method. + * @param inWidth the new width of this control. + * @param inHeight the new height of this control. + */ +void CControl::SetSize(long inX, long inY) +{ + SetSize(CPt(inX, inY)); +} + +//============================================================================= +/** + * Get the minimum allowable size of this control. + * This is used by layout managers to get the minimum size that a control is + * allowed to be when it is resizing the control. This defaults to 0,0. + * @return the minimum size that this control is allowed to be. + */ +CPt CControl::GetMinimumSize() +{ + return m_ControlData->m_MinSize; +} + +//============================================================================= +/** + * Set the minimum allowable size of this control. + * This is used by layout managers to get the minimum size that a control is + * allowed to be when it is resizing the control. This defaults to 0,0. + * param inSize the minimum size that this control is allowed to be. + */ +void CControl::SetMinimumSize(CPt inSize) +{ + if (inSize != m_ControlData->m_MinSize) { + NotifyParentNeedsLayout(); + m_ControlData->m_MinSize = inSize; + if (inSize.x > m_ControlData->m_MaxSize.x) + m_ControlData->m_MaxSize.x = inSize.x; + if (inSize.y > m_ControlData->m_MaxSize.y) + m_ControlData->m_MaxSize.y = inSize.y; + } +} + +//============================================================================= +/** + * Get the maximum allowable size of this control. + * This is used by layout managers to get the maximum size that a control is + * allowed to be when it is resizing the control. This defaults to LONG_MAX,LONG_MAX. + * @return the maximum size that this control is allowed to be. + */ +CPt CControl::GetMaximumSize() +{ + return m_ControlData->m_MaxSize; +} + +//============================================================================= +/** + * Set the maximum allowable size of this control. + * This is used by layout managers to get the maximum size that a control is + * allowed to be when it is resizing the control. This defaults to LONG_MAX,LONG_MAX. + * @param inSize the maximum size that this control is allowed to be. + */ +void CControl::SetMaximumSize(CPt inSize) +{ + if (inSize != m_ControlData->m_MaxSize) { + NotifyParentNeedsLayout(); + m_ControlData->m_MaxSize = inSize; + } +} + +//============================================================================= +/** + * Get the preferred size of this control. + * This is used by layout managers to get the preferred size of the control. + * The preferred size is often used for relative scaling of sibling controls. + * @return the preferred size of this control. + */ +CPt CControl::GetPreferredSize() +{ + return m_ControlData->m_PrefSize; +} + +//============================================================================= +/** + * Set the preferred size of this control. + * This is used by layout managers to get the preferred size of the control. + * The preferred size is often used for relative scaling of sibling controls. + * @param the preferred size of this control. + */ +void CControl::SetPreferredSize(CPt inSize) +{ + if (inSize != m_ControlData->m_PrefSize) { + NotifyParentNeedsLayout(); + Invalidate(); + m_ControlData->m_PrefSize = inSize; + } +} + +void CControl::SetAbsoluteSize(CPt inSize) +{ + SetMinimumSize(inSize); + SetMaximumSize(inSize); + SetPreferredSize(inSize); + SetSize(inSize); +} + +//============================================================================= +/** + * Event called when the mouse is moving. + * This event will be called when the mouse is over this control or when this + * control has the focus. This can be extended by controls but should be called + * if the event is to be propagated down to child controls. + * @param inPoint the location of the mouse relative to this control. + * @param inFlags Modifier keys that are down at time of event. Can be any + * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT | + * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON + */ +void CControl::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + bool theHitFlag = false; + // Go through all the children notifying them of mouse moves. + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + // If a child has not already gotten the move (higher level child covering a lower level + // one) + // then check the hit test. + if (!theHitFlag && theChild->HitTest(inPoint)) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + // This is the first child to be hit, do not allow the message to go onto another + // sibling under this one. + theHitFlag = true; + + // Do not fire events to non-enabled children. + if (theChild->IsEnabled()) { + // If this is the first time the mouse is over this then fire a mouse over + if (!theChild->IsMouseOver()) + theChild->OnMouseOver(theChildPoint, inFlags); + // Fire a mouse move as well, this will also propagate the mouse over to + // grand-children etc. + theChild->OnMouseMove(theChildPoint, inFlags); + } + } + // Check all the children and if they think the mouse is over them then notify them of a + // mouse out. + else if (theChild->IsMouseOver()) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + theChild->OnMouseOut(theChildPoint, inFlags); + } + } + + // If there is a child with focus and the mouse is not over it then still notify it of the mouse + // move + // If the mouse is over it then it would have gotten the move from the above loop. + if (m_ControlData->m_MouseFocus && !m_ControlData->m_MouseFocus->IsMouseOver()) { + CPt theChildPoint = inPoint - m_ControlData->m_MouseFocus->GetPosition(); + m_ControlData->m_MouseFocus->OnMouseMove(theChildPoint, inFlags); + } +} + +//============================================================================= +/** + * Notification that the mouse is over this control. + * This is only called once when the mouse enters the control, and does not get + * called until the mouse leaves then re-enters the control. + * @param inPoint where the mouse is, relative to this control. + * @param inFlags Modifier keys that are down at time of event. Can be any + * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT | + * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON + */ +void CControl::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + Q_UNUSED(inPoint); + Q_UNUSED(inFlags); + m_ControlData->m_IsMouseOver = true; +} + +//============================================================================= +/** + * Notification that the mouse left this control. + * This is only called once when the mouse leaves the control and does not get + * called until the mouse enters then re-leaves the control. + * This also notifies all children that the mouse is over that it is no longer + * over. + * @param inPoint where the mouse is, relative to this control. + * @param inFlags Modifier keys that are down at time of event. Can be any + * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT | + * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON + */ +void CControl::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + m_ControlData->m_IsMouseOver = false; + + // Go through all the children looking for ones that think the mouse is over. + // If it is then notify it of a mouse out. + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + if (theChild->IsMouseOver()) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + theChild->OnMouseOut(inPoint, inFlags); + } + } +} + +//============================================================================= +/** + * Notification that the left mouse button was clicked on this control. + * This handles the mouse hits and sets the focus to the control that was + * clicked on. + * @param inPoint where the mouse was clicked, in local coordinates. + * @param inFlags Modifier keys that are down at time of event. Can be any + * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT | + * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON + * @return true if the mouse event is processed. + */ +bool CControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + bool theRetVal = false; + bool theChildGotHit = false; + + // Go through all the children looking for the first one that was clicked on + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + // If the child was hit then notify it of the mouse down, and set the focus to + // be it. + if (theChild->HitTest(inPoint)) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + theChildGotHit = true; + // Only send the event if the child is enabled. + if (theChild->IsEnabled()) { + theRetVal = theChild->OnMouseDown(theChildPoint, inFlags); + + if (m_ControlData->m_Focus != theChild) { + if (m_ControlData->m_Focus) + m_ControlData->m_Focus->OnLoseFocus(); + if (theChild->CanGainFocus()) { + m_ControlData->m_Focus = theChild; + m_ControlData->m_Focus->OnGainFocus(); + } else { + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); + } + } + m_ControlData->m_MouseFocus = theChild; + } else { + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); + } + + // only want OnMouseDown to be called on the first child under the point. + break; + } + } + if (!theChildGotHit && m_ControlData->m_Focus) { + m_ControlData->m_Focus->OnLoseFocus(); + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); + } + m_ControlData->SetMouseDown(!theRetVal); + + return theRetVal; +} + +//============================================================================= +/** + * Notification that the right mouse button was clicked on this control. + * This handles the mouse hits and sets the focus to the control that was + * clicked on. + * @param inPoint where the mouse was clicked, in local coordinates. + * @param inFlags Modifier keys that are down at time of event. Can be any + * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT | + * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON + * @return true if the mouse event is processed. + */ +bool CControl::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + bool theRetVal = false; + + // Go through all the children looking for the first one that was clicked on + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + // If the child was hit then notify it of the mouse down, and set the focus to + // be it. + if (theChild->HitTest(inPoint)) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + + // Only send the event if the child is enabled. + if (theChild->IsEnabled()) { + if (m_ControlData->m_Focus != theChild) { + if (m_ControlData->m_Focus) + m_ControlData->m_Focus->OnLoseFocus(); + if (theChild->CanGainFocus()) { + m_ControlData->m_Focus = theChild; + m_ControlData->m_Focus->OnGainFocus(); + } else { + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); + } + } + m_ControlData->m_MouseFocus = theChild; + theRetVal = theChild->OnMouseRDown(theChildPoint, inFlags); + } else { + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); + } + + // only want OnMouseDown to be called on the first child under the point. + break; + } + } + + return theRetVal; +} + +//============================================================================= +/** + * Notification thatthe mouse was double clicked on this control. + * This handled the mouse hits and passes it down to all the children. + * @param inPoint where the mouse was clicked, in local coordinates. + * @param inFlags Modifier keys that are down at time of event. Can be any + * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT | + * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON + * @return true if the mouse event is processed. + */ +bool CControl::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + bool theRetVal = false; + + // Go through all the children looking for the first one that was clicked on + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + // If the child was hit then notify it of the mouse down, and set the focus to + // be it. + if (theChild->HitTest(inPoint)) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + // Only send the event if the child is enabled. + if (theChild->IsEnabled()) + theRetVal = theChild->OnMouseDoubleClick(theChildPoint, inFlags); + + // only want OnMouseDown to be called on the first child under the point. + break; + } + } + + return theRetVal; +} + +bool CControl::OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags) +{ + bool theRetVal = false; + + // try letting the focus getting the wheel first + if (m_ControlData->m_Focus) { + CPt theChildPoint = inPoint - m_ControlData->m_Focus->GetPosition(); + theRetVal = m_ControlData->m_Focus->OnMouseWheel(theChildPoint, inAmount, inFlags); + } + + // if the focus does not want the wheel then let the mouse pos do it. + if (!theRetVal) { + // Go through all the children looking for the first one that was clicked on + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + if (theChild->GetMouseWheelEventState() == ControlEventState::Listening + && theChild->IsEnabled() && theChild->HitTest(inPoint)) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + theRetVal = theChild->OnMouseWheel(theChildPoint, inAmount, inFlags); + break; + } + } + } + + return theRetVal; +} + +//============================================================================= +/** + * Notification that the mouse is hovering over this control. + * This handles the mouse hover and passes it down to all the children. + * @param inPoint where the mouse is located, in local coordinates. + * @param inFlags Modifier keys that are down at time of event. Can be any + * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT | + * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON + * @return true if the mouse event is processed. + */ +bool CControl::OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + bool theRetVal = false; + + // Go through all the children looking for the first one that was clicked on + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + // If the child was hit then notify it of the mouse down, and set the focus to + // be it. + if (theChild->HitTest(inPoint)) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + // Only send the event if the child is enabled. + if (theChild->IsEnabled()) + theRetVal = theChild->OnMouseHover(theChildPoint, inFlags); + + // only want OnMouseHover to be called on the first child under the point. + break; + } + } + + return theRetVal; +} + +//============================================================================= +/** + * Notification that the left mouse button was released. + * This is only called on the control that has focus, not on the control under + * the mouse. + * @param inPoint where the mouse was released, in local coordinates. + * @param inFlags Modifier keys that are down at time of event. Can be any + * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT | + * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON + */ +void CControl::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_ControlData->m_MouseFocus) { + CPt theChildPoint = inPoint - m_ControlData->m_MouseFocus->GetPosition(); + m_ControlData->m_MouseFocus->OnMouseUp(theChildPoint, inFlags); + } + + // Go through all the children looking for the first one that was clicked on + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + // If the child was hit then notify it of the mouse down, and set the focus to + // be it. + if (theChild->HitTest(inPoint)) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + // Only send the event if the child is enabled. + if (theChild->IsEnabled() && theChild != m_ControlData->m_MouseFocus + && theChild != m_ControlData->m_Focus) { + theChild->OnMouseUp(theChildPoint, inFlags); + } + + // only want OnMouseUp to be called on the first child under the point. + break; + } + } + m_ControlData->m_MouseFocus = std::shared_ptr<CControlData>(); + + if (m_ControlData->m_IsMouseDown) { + OnMouseClick(inPoint, inFlags); + m_ControlData->SetMouseDown(false); + } +} + +//============================================================================= +/** + * Notification that the right mouse button was released. + * This is only called on the control that has focus, not on the control under + * the mouse. + * @param inPoint where the mouse was released, in local coordinates. + * @param inFlags Modifier keys that are down at time of event. Can be any + * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT | + * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON + */ +void CControl::OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_ControlData->m_MouseFocus) { + CPt theChildPoint = inPoint - m_ControlData->m_MouseFocus->GetPosition(); + m_ControlData->m_MouseFocus->OnMouseRUp(theChildPoint, inFlags); + } + + // Go through all the children looking for the first one that was clicked on + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + // If the child was hit then notify it of the mouse down, and set the focus to + // be it. + if (theChild->HitTest(inPoint)) { + CPt theChildPoint = inPoint - theChild->GetPosition(); + // Only send the event if the child is enabled. + if (theChild->IsEnabled() && theChild != m_ControlData->m_MouseFocus + && theChild != m_ControlData->m_Focus) { + theChild->OnMouseRUp(theChildPoint, inFlags); + } + + // only want OnMouseUp to be called on the first child under the point. + break; + } + } + m_ControlData->m_MouseFocus = std::shared_ptr<CControlData>(); +} + +//============================================================================= +/** + * Handles character input from the keyboard. + * + * @param inChar Character that was pressed + * @return true if the character was handled, false if this control does not + * care about the character that was pressed + */ +bool CControl::OnChar(const QString &inChar, Qt::KeyboardModifiers inModifiers) +{ + if (m_ControlData->m_Focus) + return m_ControlData->m_Focus->OnChar(inChar, inModifiers); + else + return false; +} + +//============================================================================= +/** + * Handles a key down message from the keyboard. + * + * @param inChar Character that was pressed + * @return true if the character was handled, false if this control does not + * care about the character that was pressed + */ +bool CControl::OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inModifiers) +{ + bool theRetVal = false; + + if (m_ControlData->m_Focus) + theRetVal = m_ControlData->m_Focus->OnKeyDown(inChar, inModifiers); + + if (!theRetVal) { + if (inChar == Qt::Key_Tab) { + if (inModifiers & Qt::ShiftModifier) + OnReverseTab(); + else + OnTab(); + theRetVal = true; + } + } + + return theRetVal; +} + +//============================================================================= +/** + * Handles a key up from the keyboard. + * + * @param inChar Character that was pressed + * @return true if the character was handled, false if this control does not + * care about the character that was pressed + */ +bool CControl::OnKeyUp(unsigned int inChar, Qt::KeyboardModifiers) +{ + Q_UNUSED(inChar); + return false; +} + +//============================================================================= +/** + * Find the first child (descendant) control that has a valid drop target. + */ +CDropTarget *CControl::FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags, + EStudioObjectType objectType, + Q3DStudio::DocumentEditorFileType::Enum fileType) +{ + CDropTarget *theDropTarget = NULL; + + // Go through all the children looking for the first one that was clicked on + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + if (theChild->IsMouseOver() && theChild->IsEnabled()) { + // Put the point into this childs coords. + CPt theChildPoint = inMousePoint - theChild->GetPosition(); + + // Allow the child the opportunity to respond + theDropTarget = theChild->FindDropCandidate(theChildPoint, inFlags, objectType, + fileType); + + if (theDropTarget) + break; + } + } + return theDropTarget; +} + +//============================================================================= +/** + * Add a child control to this control. + * This will make the child behave as a child of this, get set up for drawing + * and events. The inInsertBefore control is used to determine Z-depth, or + * manually insert a control into a specific location. + * The child cannot already be a child of another control. + * @param inControl the control to be added. + * @param inInsertBefore the control to be inserted before, or std::shared_ptr<CControlData>() to + * be at the back.\ + */ +void CControl::AddChild(CControl *inControl, + CControl *inInsertBefore /*=std::shared_ptr<CControlData>()*/) +{ + NotifyParentNeedsLayout(); + ControlGraph::AddChild(*this, *inControl, inInsertBefore); +} + +//============================================================================= +/** + * Remove a child control from this control. + * This will remove it from drawing and getting any events. + * @param inControl the control to be removed. + */ +void CControl::RemoveChild(CControl *inControl) +{ + if (inControl) { + ControlGraph::RemoveChild(*this, *inControl); + + if (m_ControlData->m_Focus == inControl->m_ControlData) + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); + if (m_ControlData->m_MouseFocus == inControl->m_ControlData) + m_ControlData->m_MouseFocus = std::shared_ptr<CControlData>(); + } +} + +//============================================================================= +/** + * Remove all child controls from this control. + * This will remove it from drawing and getting any events. + * + * This is not recursive + */ +void CControl::RemoveAllChildren() +{ + NotifyParentNeedsLayout(); + ControlGraph::RemoveAllChildren(*this); + + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); + m_ControlData->m_MouseFocus = std::shared_ptr<CControlData>(); +} + +//============================================================================= +/** + * Retrieve the index of a child control. The index will return the zero based position. + * @param inChildControl the control that is a direct child of this control + * @return the zero-based index of this control we will return -1 if we don't find the control + */ +long CControl::GetChildIndex(CControl *inChildControl) +{ + return ControlGraph::GetChildIndex(*this, *inChildControl); +} + +static inline CControl *ToControl(std::shared_ptr<CControlData> inPtr) +{ + if (inPtr) + return inPtr->GetControl(); + return nullptr; +} + +//============================================================================= +/** + * Finds a child control by its name + * @return the child if found, std::shared_ptr<CControlData>() otherwise + */ +CControl *CControl::FindChildByName(const Q3DStudio::CString &inName) +{ + std::shared_ptr<CControlData> theResult = std::shared_ptr<CControlData>(); + + ControlGraph::SIterator theChildIter = GetChildren(); + for (; !theChildIter.IsDone(); ++theChildIter) { + if (theChildIter.GetCurrent()->GetName() == inName) { + theResult = theChildIter.GetCurrent(); + break; + } + } + + return ToControl(theResult); +} + +CControl *CControl::FocusedChild() +{ + CControl *theResult = nullptr; + + ControlGraph::SIterator theChildIter = GetChildren(); + for (; !theChildIter.IsDone(); ++theChildIter) { + auto current = ToControl(theChildIter.GetCurrent()); + auto hasFocus = HasFocus(current); + if (hasFocus) { + if (current->GetFirstChild()) + theResult = current->FocusedChild(); + else + theResult = current; + break; + } + } + + return theResult; +} + +//============================================================================= +/** + * Check to see if the mouse is over this control or not. + * @return true if the mouse is over this control. + */ +bool CControl::IsMouseOver() const +{ + return m_ControlData->m_IsMouseOver; +} + +//============================================================================= +/** + * Check to see if inPoint is over this control or not. + * This is used for mouse hits and can be extended for non-standard control + * shapes. Non-visible controls always return false. + * @param inPoint the location of the mouse in local coordinates. + */ +bool CControl::HitTest(const CPt &inPoint) const +{ + CPt thePoint = inPoint - GetPosition(); + CPt theSize = GetSize(); + // Basic check to see if it's in the size. + if (IsVisible() && thePoint.x >= 0 && thePoint.y >= 0 && thePoint.x < theSize.x + && thePoint.y < theSize.y) { + return true; + } + return false; +} + +//============================================================================= +/** + * Checks to see if any part of this control is in the rect. + * This is used for drawing and ignoring objects that do not need to be + * redrawn or need to be drawn. + * @param inRect the rect to check to see if this is in. + * @return true if this is in the rect. + */ +bool CControl::IsInRect(const CRct &inRect) const +{ + CRct myRect(GetPosition(), GetSize()); + + if (myRect.position <= inRect.size + inRect.position) { + if (myRect.size + myRect.position >= inRect.position) + return true; + } + return false; +} + +//============================================================================= +/** + * Invalidate this control and cause it to be redrawn. + * @param inInvalidate true if this is to be invalidated. + */ +void CControl::Invalidate(bool inInvalidate /*= true*/) +{ + m_ControlData->m_IsInvalidated = inInvalidate; + if (inInvalidate && GetParent() != nullptr) + GetParent()->OnChildInvalidated(); + if (!inInvalidate) + m_ControlData->m_IsChildInvalidated = false; +} + +//============================================================================= +/** + * Invalidate this object and all children within inRect. + * @param inRect the rect in which to invalidate all children. + */ +void CControl::InvalidateRect(const CRct &inRect) +{ + Invalidate(); + + ControlGraph::SIterator thePos = GetChildren(); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + if (theChild->IsInRect(inRect)) { + CRct theChildRect = inRect; + theChildRect.Offset(theChild->GetPosition()); + + theChild->InvalidateRect(inRect); + } + } +} + +//============================================================================= +/** + * Check to see if this control is invalidated or not. + * @return true if this control is invalidated. + */ +bool CControl::IsInvalidated() const +{ + return m_ControlData->IsInvalidated(); +} + +//============================================================================= +/** + * Notifies this control that a child of it has been invalidated. + */ +void CControl::OnChildInvalidated() +{ + // Only do it if we haven't already, avoid multiple traversals up the tree. + if (!m_ControlData->m_IsChildInvalidated) { + if (GetParent() != nullptr) + GetParent()->OnChildInvalidated(); + else if (m_ControlData->m_WindowListener != nullptr) + m_ControlData->m_WindowListener->OnControlInvalidated(); + + m_ControlData->m_IsChildInvalidated = true; + } +} + +//============================================================================= +/** + * Checks to see if a child of this control is invalidated. + */ +bool CControl::IsChildInvalidated() const +{ + return m_ControlData->m_IsChildInvalidated || m_ControlData->m_IsInvalidated; +} + +//============================================================================= +/** + * Set this control as being visible or not. + * If the control is not visible then it will not be drawn and will not + * get mouse clicks. + * @param inIsVisible true if this control is to be visible. + */ +void CControl::SetVisible(bool inIsVisible) +{ + if (inIsVisible != m_ControlData->m_IsVisible) { + m_ControlData->m_IsVisible = inIsVisible; + NotifyParentNeedsLayout(); + + if (GetParent() != nullptr) + GetParent()->OnChildSizeChanged(this); + + OnVisibleStateChange(inIsVisible); + OnParentVisibleStateChanged(inIsVisible); + + this->Invalidate(); + } +} + +//============================================================================= +/** + * Notification that the visible state of a control has changed + */ +void CControl::OnVisibleStateChange(bool inIsVisible) +{ + Q_UNUSED(inIsVisible); +} + +void CControl::OnParentVisibleStateChanged(bool inIsVisible) +{ + NotifyParentNeedsLayout(); + ControlGraph::SIterator thePos = GetChildren(); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + theChild->OnParentVisibleStateChanged(inIsVisible); + } +} + +//============================================================================= +/** + * Checks to see if this control is visible or not. + * If the control is not visible then it will not be drawn and will not + * get mouse clicks. + * @return true if this control is visible. + */ +bool CControl::IsVisible() const +{ + return m_ControlData->m_IsVisible; +} + +//============================================================================= +/** + * Sets whether or not this control is enabled. + * If the control is not enabled then it is still drawn and still intercepts + * mouse clicks, but it will not actually process them. + * @param inIsEnabled true if this control is to be enabled. + */ +void CControl::SetEnabled(bool inIsEnabled) +{ + if (inIsEnabled != m_ControlData->m_IsEnabled) { + NotifyParentNeedsLayout(); + m_ControlData->m_IsEnabled = inIsEnabled; + + ControlGraph::SIterator thePos = GetChildren(); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + theChild->SetParentEnabled(inIsEnabled); + } + Invalidate(); + } +} + +void CControl::SetParentEnabled(bool inParentEnabled) +{ + NotifyParentNeedsLayout(); + m_ControlData->m_IsParentEnabled = inParentEnabled; + ControlGraph::SIterator thePos = GetChildren(); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + theChild->SetParentEnabled(inParentEnabled); + } + Invalidate(); +} + +//============================================================================= +/** + * Gets whether or not this control is enabled. + * If the control is not enabled then it is still drawn and still intercepts + * mouse clicks, but it will not actually process them. + * @param inIsEnabled true if this control is to be enabled. + */ +bool CControl::IsEnabled() const +{ + return m_ControlData->IsEnabled(); +} + +//============================================================================= +/** + * Gets teh value of the enabled flag without its parent's flag + */ +bool CControl::GetEnabledFlag() +{ + return m_ControlData->GetEnabledFlag(); +} + +//============================================================================= +/** + * Sets the enabled flag...this is used when a control wants to override the + * SetEnabled function and does not necessarily want to pass enabled messages + * to its children no matter what + */ +void CControl::SetEnabledFlag(bool inIsEnabled) +{ + m_ControlData->SetEnabledFlag(inIsEnabled); + Invalidate(); +} + +//============================================================================= +/** + * Notification that the size of a child has changed. + * @param inControl the control that has changed size. + */ +void CControl::OnChildSizeChanged(CControl *inControl) +{ + Q_UNUSED(inControl); +} + +void CControl::SetName(const QString &inName) +{ + m_ControlData->SetName(Q3DStudio::CString::fromQString(inName)); +} + +QString CControl::GetName() +{ + return m_ControlData->GetName().toQString(); +} + +void CControl::BeginDrawChildren(CRenderer *inRenderer) +{ + Q_UNUSED(inRenderer); +} + +long CControl::DoPopup(QMenu *inMenu, CPt inLocation) +{ + inLocation.Offset(GetPosition()); + CControl *theParent(GetParent()); + if (theParent) + return theParent->DoPopup(inMenu, inLocation); + else + return m_ControlData->m_WindowListener->DoPopup(inMenu, inLocation); +} + +//============================================================================= +/** + * Called when a control acquires focus + */ +void CControl::OnGainFocus() +{ +} + +//============================================================================= +/** + * Causes focus to be lost. + */ +void CControl::OnLoseFocus() +{ + if (m_ControlData->m_Focus) + m_ControlData->m_Focus->OnLoseFocus(); + FireFocusEvent(false); + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); +} + +//============================================================================= +/** + * @return the parent control of this class + */ +CControl *CControl::GetParent() +{ + return m_ControlData->GetParent(); +} + +const CControl *CControl::GetParent() const +{ + return m_ControlData->GetParent(); +} + +//============================================================================= +/** + * Removes a control from the top level control + */ +void CControl::RemoveUberControl(CControl *inControl) +{ + if (GetParent() != nullptr) + GetParent()->RemoveUberControl(inControl); + else + RemoveChild(inControl); + Invalidate(); +} + +long CControl::GetChildCount() +{ + return ControlGraph::GetNumChildren(*this); +} + +Q3DStudio::Control::ControlGraph::SIterator CControl::GetChildren() +{ + return ControlGraph::GetChildren(*this); +} + +Q3DStudio::Control::ControlGraph::SReverseIterator CControl::GetReverseChildren() +{ + return ControlGraph::GetRChildren(*this); +} + +//============================================================================= +/** + * Gets the global position of the point in regards to the top level control + */ +CPt CControl::GetGlobalPosition(CPt inChildPoint) const +{ + CPt thePosition(GetPosition()); + CPt thePoint = CPt(inChildPoint.x + thePosition.x, inChildPoint.y + thePosition.y); + if (GetParent()) + return GetParent()->GetGlobalPosition(thePoint); + else + return thePoint; +} + +//============================================================================= +/** + * Query the platform specific render device (window) + */ +Qt3DSRenderDevice CControl::GetPlatformDevice() +{ + if (GetParent()) + return GetParent()->GetPlatformDevice(); + return nullptr; +} + +//============================================================================= +/** + * Does self or child use this render device? + * @see CWndControl::OnKillFocus + */ +bool CControl::IsChildPlatformDevice(Qt3DSRenderDevice inDevice) +{ + if (GetPlatformDevice() == inDevice) + return true; + + ControlGraph::SIterator thePos = GetChildren(); + for (; thePos.HasNext(); ++thePos) { + std::shared_ptr<CControlData> theChild = (*thePos); + if (theChild->IsChildPlatformDevice(inDevice)) + return true; + } + + return false; +} + +//============================================================================= +/** + * Shows the window's moveable window with text + * + * @param inLocation the postion of hte center point of the window + * @param inText the text the window will display + */ +void CControl::ShowMoveableWindow(CPt inLocation, const Q3DStudio::CString &inText, + CRct inBoundingRct) +{ + CPt thePosition(GetPosition()); + CPt thePoint = CPt(inLocation.x + thePosition.x, inLocation.y + thePosition.y); + + if (GetParent()) + GetParent()->ShowMoveableWindow(thePoint, inText, inBoundingRct); + else if (m_ControlData->m_WindowListener) + m_ControlData->m_WindowListener->ShowMoveableWindow(thePoint, inText, inBoundingRct); +} + +//============================================================================= +/** + * Hides the window's moveable window + */ +void CControl::HideMoveableWindow() +{ + if (GetParent() != nullptr) + GetParent()->HideMoveableWindow(); + else if (m_ControlData->m_WindowListener) + m_ControlData->m_WindowListener->HideMoveableWindow(); +} + +//============================================================================= +/** + * Offsets the position of this control... this is useful if you don't want to calculate + * the global position every time + */ +void CControl::OffsetPosition(CPt inOffset) +{ + CPt thePosition(GetPosition()); + thePosition.Offset(inOffset); + SetPosition(thePosition); +} + +//============================================================================= +/** + * Gets the first child of this control + */ +CControl *CControl::GetFirstChild() +{ + std::shared_ptr<CControlData> theChild = std::shared_ptr<CControlData>(); + ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this); + if (!thePos.IsDone()) + theChild = (*thePos); + return ToControl(theChild); +} + +//============================================================================= +/** + * @return true if this control has focus + */ +bool CControl::HasFocus(CControl *inControl) +{ + return (ToControl(m_ControlData->m_Focus) == inControl); +} + +//============================================================================= +/** + * default is true for controls... override if the control cannot have focus + */ +bool CControl::CanGainFocus() +{ + if (IsVisible()) { + for (ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this); !thePos.IsDone(); + ++thePos) { + if ((*thePos)->CanGainFocus()) + return true; + } + } + return false; +} + +//============================================================================= +/** + * Returns true if this CControl is in focus. + * @return True if this CControl is in focus. + */ +bool CControl::IsInFocus() +{ + if (GetParent()) + return GetParent()->HasFocus(this); + + return false; +} + +//============================================================================= +/** + * Handles the tab button in controls + */ +void CControl::OnTab() +{ + // Go through the children... if there is a focus, then get the next control + ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this); + bool theFoundFlag = false; + if (m_ControlData->m_Focus) { + while (!thePos.IsDone() && !theFoundFlag) { + std::shared_ptr<CControlData> theCurrentControl = (*thePos); + if (theCurrentControl == m_ControlData->m_Focus) { + ++thePos; + while (!thePos.IsDone() && !theFoundFlag) { + std::shared_ptr<CControlData> theNextFocusCanidate = (*thePos); + if (theNextFocusCanidate->CanGainFocus()) { + m_ControlData->m_Focus->OnLoseFocus(); + theFoundFlag = true; + m_ControlData->m_Focus = theNextFocusCanidate; + m_ControlData->m_Focus->SetFocusToFirstAvailable(); + } else { + ++thePos; + } + } + } else { + ++thePos; + } + } + // If we didn't find it and we have a parent, then allow the parent to decide + if (!theFoundFlag) { + m_ControlData->m_Focus->OnLoseFocus(); + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); + if (GetParent()) + GetParent()->OnTab(); + else + SetFocusToFirstAvailable(); + } + } else { + // If no focus, then go to first available control + SetFocusToFirstAvailable(); + } +} + +//============================================================================= +/** + * Handles the shift tab button in controls + */ +void CControl::OnReverseTab() +{ + // Go through the children in reverse order... if there is a focus, then get the next control + ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + bool theFoundFlag = false; + if (m_ControlData->m_Focus) { + while (!thePos.IsDone() && !theFoundFlag) { + std::shared_ptr<CControlData> theCurrentControl = (*thePos); + if (theCurrentControl == m_ControlData->m_Focus) { + ++thePos; + while (!thePos.IsDone() && !theFoundFlag) { + std::shared_ptr<CControlData> theNextFocusCanidate = (*thePos); + if (theNextFocusCanidate->CanGainFocus()) { + m_ControlData->m_Focus->OnLoseFocus(); + theFoundFlag = true; + m_ControlData->m_Focus = theNextFocusCanidate; + m_ControlData->m_Focus->SetFocusToLastAvailable(); + } else { + ++thePos; + } + } + } else { + ++thePos; + } + } + // If we didn't find it and we have a parent, then allow the parent to decide + if (!theFoundFlag) { + m_ControlData->m_Focus->OnLoseFocus(); + m_ControlData->m_Focus = std::shared_ptr<CControlData>(); + if (GetParent()) + GetParent()->OnReverseTab(); + else + SetFocusToLastAvailable(); + } + } else { + // If no focus, then go to last available control + SetFocusToLastAvailable(); + } +} + +//============================================================================= +/** + * Sets the focus to the first available child control + */ +void CControl::SetFocusToFirstAvailable() +{ + bool theFlag = false; + OnGainFocus(); + + // if there are any child controls, then go through them + for (ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this); + !thePos.IsDone() && !theFlag; ++thePos) { + std::shared_ptr<CControlData> theControl = (*thePos); + if (theControl->CanGainFocus()) { + m_ControlData->m_Focus = theControl; + m_ControlData->m_Focus->SetFocusToFirstAvailable(); + theFlag = true; + } + } +} + +//============================================================================= +/** + * Sets the focus to the last available child control + */ +void CControl::SetFocusToLastAvailable() +{ + bool theFlag = false; + OnGainFocus(); + + // if there are any child controls, then go through them + for (ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this); + !thePos.IsDone() && !theFlag; ++thePos) { + std::shared_ptr<CControlData> theControl = (*thePos); + if (theControl->CanGainFocus()) { + m_ControlData->m_Focus = theControl; + m_ControlData->m_Focus->SetFocusToLastAvailable(); + theFlag = true; + } + } +} + +//=============================================================================== +/** +* Coverts the given point from local coordinates into global coordinates. +* @param inPoint the point in local coordinates to be converted +* @return The point translated to global coordinates +*/ +CPt CControl::ClientToScreen(CPt inPoint) +{ + CPt theFinalPt = inPoint + m_ControlData->m_Position; + + if (GetParent()) + theFinalPt = GetParent()->ClientToScreen(theFinalPt); + else if (m_ControlData->m_WindowListener) + theFinalPt = m_ControlData->m_WindowListener->ClientToScreen(theFinalPt); + + return theFinalPt; +} + +//=============================================================================== +/** +* Coverts the given point from screen coordinates into local space. +* @param inPoint the point in screen coordinates to be converted +* @return The point translated to local, client coordinates +*/ +CPt CControl::ScreenToClient(CPt inPoint) +{ + CPt theFinalPt = inPoint - m_ControlData->m_Position; + + if (GetParent()) + theFinalPt = GetParent()->ScreenToClient(theFinalPt); + else if (m_ControlData->m_WindowListener) + theFinalPt = m_ControlData->m_WindowListener->ScreenToClient(theFinalPt); + + return theFinalPt; +} + +//=============================================================================== +/** +* Adds a focus listener to this control +*/ +void CControl::AddFocusListener(CChildFocusListener *inListener) +{ + m_ControlData->m_FocusListeners.AddListener(inListener); +} + +//=============================================================================== +/** +* Removes a focus listener to this control +*/ +void CControl::RemoveFocusListener(CChildFocusListener *inListener) +{ + m_ControlData->m_FocusListeners.RemoveListener(inListener); +} + +//=============================================================================== +/** +* tells anyone listeneing that the focus of this control has changed +*/ +void CControl::FireFocusEvent(bool inStatus) +{ + m_ControlData->m_FocusListeners.FireEvent(&CChildFocusListener::OnChildFocusChange, inStatus); +} + +//=============================================================================== +/** + * Get the platform specific view that this is embedded into. + * Used for when platform dependent controls have to be embedded or used. + */ +TPlatformView CControl::GetPlatformView() +{ + if (GetParent()) + return GetParent()->GetPlatformView(); + else if (m_ControlData->m_WindowListener) + return m_ControlData->m_WindowListener->GetPlatformView(); + return nullptr; +} + +bool CControl::IsMouseDown() +{ + return m_ControlData->m_IsMouseDown; +} + +void CControl::OnMouseClick(CPt, Qt::KeyboardModifiers) +{ +} + +void CControl::GrabFocus(CControl *inControl) +{ + if (GetParent()) + GetParent()->GrabFocus(this); + + std::shared_ptr<CControlData> theNewFocus; + if (inControl) + theNewFocus = inControl->m_ControlData; + + if (m_ControlData->m_Focus != theNewFocus) { + if (m_ControlData->m_Focus) + m_ControlData->m_Focus->OnLoseFocus(); + m_ControlData->m_Focus = theNewFocus; + if (m_ControlData->m_Focus) + m_ControlData->m_Focus->OnGainFocus(); + } +} + +//=============================================================================== +/** + * Used to notify scrolling views that something should be visible. + */ +void CControl::EnsureVisible(CRct inRect) +{ + if (GetParent()) { + inRect.Offset(GetPosition()); + GetParent()->EnsureVisible(inRect); + } +} + +//=============================================================================== +/** + * Call to make this control visible. + */ +void CControl::EnsureVisible() +{ + CRct theRect(CPt(0, 0), GetSize()); + EnsureVisible(theRect); +} + +void CControl::OnParentChanged(CControl * /*inNewParent*/) +{ +} + +void CControl::MarkChildrenNeedLayout() +{ + if (m_ControlData->m_ChildrenNeedLayout == false) { + for (ControlGraph::SIterator theIter = GetChildren(); theIter.IsDone() == false; ++theIter) + (*theIter)->MarkNeedsLayout(); + m_ControlData->m_ChildrenNeedLayout = true; + } +} + +void CControl::NotifyParentNeedsLayout() +{ + CControl *theParent(GetParent()); + if (theParent) + theParent->MarkChildrenNeedLayout(); +} + +void CControl::MarkNeedsLayout() +{ + m_ControlData->m_NeedsLayout = true; +} +// Tell this control that one of its children need to be layed out. +void CControl::SetLayout(CPt inSize, CPt inPosition) +{ + SetSize(inSize); + SetPosition(inPosition); + m_ControlData->m_NeedsLayout = false; +} + +void CControl::LayoutChildren() +{ + for (ControlGraph::SIterator theIter = GetChildren(); theIter.IsDone() == false; ++theIter) { + std::shared_ptr<CControlData> theChild(*theIter); + // By default the children get the exact same layout that I do. + theChild->SetLayout(theChild->m_Size, theChild->m_Position); + } + m_ControlData->m_ChildrenNeedLayout = false; +} + +void CControl::EnsureLayout() +{ + if (m_ControlData->m_NeedsLayout) { + if (IsVisible()) { + CControl *parent = GetParent(); + if (parent) + parent->LayoutChildren(); + } + m_ControlData->m_NeedsLayout = false; + } + if (m_ControlData->m_ChildrenNeedLayout) { + LayoutChildren(); + m_ControlData->m_ChildrenNeedLayout = false; + } +} + +void CControl::ChildrenChanged() +{ + MarkChildrenNeedLayout(); + Invalidate(); + m_ControlData->OnHierarchyChanged(); +} + +void CControl::setCursorIfNotSet(long cursor) +{ + if (cursor != m_cursorSet) { + if (m_cursorSet != -1) + qApp->changeOverrideCursor(CResourceCache::GetInstance()->GetCursor(cursor)); + else + qApp->setOverrideCursor(CResourceCache::GetInstance()->GetCursor(cursor)); + m_cursorSet = cursor; + } +} + +void CControl::resetCursor() +{ + if (m_cursorSet != -1) { + // Restoring back to no-override state seems to not change the cursor automatically + // to the default cursor, so let's do that manually before restoring the cursor + qApp->changeOverrideCursor(Qt::ArrowCursor); + qApp->restoreOverrideCursor(); + m_cursorSet = -1; + } +} + +QCursor CControl::getCursor() const +{ + if (m_cursorSet != -1) + return CResourceCache::GetInstance()->GetCursor(m_cursorSet); + return QCursor(); +} |