diff options
Diffstat (limited to 'src/Authoring/Qt3DStudio/Controls')
21 files changed, 5230 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Controls/AppFonts.cpp b/src/Authoring/Qt3DStudio/Controls/AppFonts.cpp new file mode 100644 index 00000000..6b491cf1 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/AppFonts.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** 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 "AppFonts.h" +#include "StudioPreferences.h" + +/** + * Constructor for a CAppFonts object. + */ +//============================================================================= +CAppFonts::CAppFonts() +{ + // Normal font + const QString theFontFace = CStudioPreferences::GetFontFaceName(); + + m_NormalFont.setFamily(theFontFace); + m_NormalFont.setPointSize(13); +} + +//============================================================================== +/** + * Releases the object. + */ +//============================================================================== +CAppFonts::~CAppFonts() +{ +} + +//============================================================================== +/** + * Returns a pointer to the only instance of this class. Automatically cleaned + * up when the program exits. + */ +//============================================================================== +CAppFonts *CAppFonts::GetInstance() +{ + static CAppFonts theAppFonts; + return &theAppFonts; +} + +//============================================================================== +/** + * Returns the normal font for use on GUI elements for the application. + */ +//============================================================================== +QFont CAppFonts::GetNormalFont() +{ + return m_NormalFont; +} diff --git a/src/Authoring/Qt3DStudio/Controls/AppFonts.h b/src/Authoring/Qt3DStudio/Controls/AppFonts.h new file mode 100644 index 00000000..a3c10b01 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/AppFonts.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +//============================================================================== +// Includes +//============================================================================== + +#ifndef INCLUDED_APP_FONTS_H +#define INCLUDED_APP_FONTS_H 1 + +#pragma once + +//============================================================================== +// Includes +//============================================================================== + +#include <QtGui/qfont.h> +//============================================================================== +/** + * Dispatches commands + */ +//============================================================================== +class CAppFonts +{ +public: + CAppFonts(); + virtual ~CAppFonts(); + static CAppFonts *GetInstance(); + QFont GetNormalFont(); + +private: + QFont m_NormalFont; +}; + +#endif // INCLUDED_APP_FONTS_H diff --git a/src/Authoring/Qt3DStudio/Controls/BufferedRenderer.cpp b/src/Authoring/Qt3DStudio/Controls/BufferedRenderer.cpp new file mode 100644 index 00000000..5a97f0d3 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/BufferedRenderer.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "BufferedRenderer.h" +#include "AppFonts.h" + + +//============================================================================= +/** + * Create a buffered renderer. + * @param inSize the size of this renderer. + */ +CBufferedRenderer::CBufferedRenderer(const QSize &inSize) + : CWinRenderer() +{ + m_CurrentBitmap = QPixmap(inSize); + + // KDAB_TODO: I don't see why m_OldBitmap is needed, nor what is the difference between CBufferedRenderer and COffscreenRenderer + m_OldBitmap = m_CurrentBitmap; + + m_painter = new QPainter(&m_CurrentBitmap); + QFont font = CAppFonts::GetInstance()->GetNormalFont(); + m_painter->setFont(font); + + m_Size = inSize; + + PushClippingRect(QRect(QPoint(0,0), inSize)); +} + + +//============================================================================= +/** + * Destructor + */ +CBufferedRenderer::~CBufferedRenderer() +{ + delete m_painter; + m_painter = nullptr; +} + +//============================================================================= +/** + * Copy this BufferedRenderer to inRenderer using alpha blend. + * This will semi-transparently copy the contents of this buffer to inRenderer + * using the alpha. + * @param inRenderer the destination renderer. + * @param inAlpha the opacity, 255 = opaque 0 = transparent. + */ +/*void CBufferedRenderer::TransparentBltTo( CRenderer* inRenderer, short inAlpha ) +{ + + CPt theDestination; + theDestination.Offset( inRenderer->GetTranslation( ) ); + + AlphaBlendExt( inRenderer->GetGraphicsDevice( )->m_hDC, (short)theDestination.x, +(short)theDestination.y, (short)m_Size.x, (short)m_Size.y, m_DC->m_hDC, 0, 0, (short)m_Size.x, +(short)m_Size.y, inAlpha ); +// ::DrawAlphaBlendBitmap( inRenderer->GetDC( ), (short)theDestination.x, +(short)theDestination.y, m_CurrentBitmap, (short)m_Size.x, (short)m_Size.y, 0, 0, inAlpha ); + +}*/ + +//============================================================================== +/** + * Draws a bitmap using alpha blending. Works in NT/2000/9x + * + * @param inDCDest Destination device context + * @param inX X coordinate for drawing + * @param inY Y coordinate for drawing + * @param inWidth Drawing width + * @param inHeight Drawing height + * @param inDCSrc Source device context + * @param inSourceX X source coordinate + * @param inSourceY Y source coordinate + * @param inSourceWidth Source drawing width + * @param inSourceHeight Source drawing height + * @param inAlpha Alpha blend: 0-255, where 255 is opaque + */ +//============================================================================== +/*bool CBufferedRenderer::AlphaBlendExt( HDC inDCDest, short inX, short inY, short inWidth, short +inHeight, HDC inDCSrc, short inSourceX, short inSourceY, short inSourceWidth, short inSourceHeight, +short inAlpha ) +{ + BITMAPINFOHEADER theBMI; + + theBMI.biSize = sizeof( BITMAPINFOHEADER ); + theBMI.biWidth = inWidth; + theBMI.biHeight = inHeight; + theBMI.biPlanes = 1; + theBMI.biBitCount = 32; // 24 bits + alpha channel + theBMI.biCompression = BI_RGB; // no compression + theBMI.biSizeImage = 0; + theBMI.biXPelsPerMeter = 0; + theBMI.biYPelsPerMeter = 0; + theBMI.biClrUsed = 0; // use the whole palette + theBMI.biClrImportant = 0; + + BYTE* theSrcBits; + HBITMAP theBmpSrc; + + // Create DIB section in shared memory + theBmpSrc = CreateDIBSection( inDCSrc, ( BITMAPINFO* ) &theBMI, DIB_RGB_COLORS, ( void** ) +&theSrcBits, 0, 0L ); + + BYTE* theDestBits; + HBITMAP theBmpDest; + + // Create DIB section in shared memory + theBmpDest = CreateDIBSection( inDCDest, ( BITMAPINFO* ) &theBMI, DIB_RGB_COLORS, ( void** ) +&theDestBits, 0, 0L ); + + // Copy our source and destination bitmaps onto our DIBSections. + // so we can get access to their bits using the BYTE*'s we used + // in the CreateDIBSection()s above. + + HDC theDC = CreateCompatibleDC( nullptr ); + + HBITMAP theDCOld = ( HBITMAP ) SelectObject( theDC, theBmpSrc ); + + if ( !StretchBlt( theDC, 0, 0, inWidth, inHeight, inDCSrc, inSourceX, inSourceY, +inSourceWidth, inSourceHeight, SRCCOPY ) ) + { + SelectObject( theDC, theDCOld ); + DeleteDC( theDC ); + DeleteObject( theBmpSrc ); + DeleteObject( theBmpDest ); + return false; + } + + SelectObject( theDC, theBmpDest ); + + if (! StretchBlt( theDC, 0, 0, inWidth, inHeight, inDCDest, inX, inY, inWidth, inHeight, +SRCCOPY ) ) + { + SelectObject( theDC, theDCOld ); + DeleteDC( theDC ); + DeleteObject( theBmpSrc ); + DeleteObject( theBmpDest ); + return true; + } + + SelectObject( theDC, theDCOld ); + DeleteDC( theDC ); + + short theXLoop, theYLoop; + + for( theYLoop=0; theYLoop<inHeight; ++theYLoop ) + { + LPBYTE theDestRGB = ( LPBYTE ) &( ( DWORD* ) theDestBits)[theYLoop * inWidth]; + LPBYTE theSrcRGB = ( LPBYTE ) &( ( DWORD* ) theSrcBits)[theYLoop * inWidth]; + + for( theXLoop=0; theXLoop<inWidth; theXLoop++ ) + { + theSrcRGB[0] = ( BYTE ) ( ( theDestRGB[0] * ( 255 - inAlpha ) + theSrcRGB[0] +* inAlpha ) >> 8 ); + theSrcRGB[1] = ( BYTE ) ( ( theDestRGB[1] * ( 255 - inAlpha ) + theSrcRGB[1] +* inAlpha ) >> 8 ); + theSrcRGB[2] = ( BYTE ) ( ( theDestRGB[2] * ( 255 - inAlpha ) + theSrcRGB[2] +* inAlpha ) >> 8 ); + + theSrcRGB += 4; + theDestRGB += 4; + } + } + + theDC = CreateCompatibleDC( nullptr ); + + theDCOld = ( HBITMAP ) SelectObject( theDC, theBmpSrc ); + + if ( !BitBlt( inDCDest, inX, inY, inWidth, inHeight, theDC, 0, 0, SRCCOPY ) ) + { + SelectObject( theDC, theDCOld ); + DeleteDC( theDC ); + DeleteObject( theBmpSrc ); + DeleteObject( theBmpDest ); + return false; + } + + SelectObject( theDC, theDCOld ); + DeleteDC( theDC ); + + DeleteObject( theBmpSrc ); + DeleteObject( theBmpDest ); + + return true; +}*/ diff --git a/src/Authoring/Qt3DStudio/Controls/BufferedRenderer.h b/src/Authoring/Qt3DStudio/Controls/BufferedRenderer.h new file mode 100644 index 00000000..19cc8f78 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/BufferedRenderer.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#ifndef INCLUDED_BUFFERED_RENDERER_H +#define INCLUDED_BUFFERED_RENDERER_H 1 + +#pragma once + +#include "WinRenderer.h" + +#include <QPainter> +#include <QPixmap> +#include <QFont> + +class CBufferedRenderer : public CWinRenderer +{ +public: + CBufferedRenderer(const QSize &inSize); + + virtual ~CBufferedRenderer(); + QPixmap pixmap() const override { return m_CurrentBitmap;} + + // void TransparentBltTo( CRenderer* inRenderer, short inAlpha ); + +protected: + QPixmap m_OldBitmap; + QPixmap m_CurrentBitmap; + QSize m_Size; + + // bool AlphaBlendExt( HDC inDCDest, short inX, short inY, short inWidth, short inHeight, HDC + //inDCSrc, short inSourceX, short inSourceY, short inSourceWidth, short inSourceHeight, short + //inAlpha ); +}; +#endif // INCLUDED_BUFFERED_RENDERER_H diff --git a/src/Authoring/Qt3DStudio/Controls/ClickableLabel.cpp b/src/Authoring/Qt3DStudio/Controls/ClickableLabel.cpp new file mode 100644 index 00000000..73995fcf --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/ClickableLabel.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** 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 "ClickableLabel.h" + +#include <QMouseEvent> + +ClickableLabel::ClickableLabel(QWidget *pr) : + QLabel(pr) +{ + +} + +void ClickableLabel::mousePressEvent(QMouseEvent *event) +{ + if (rect().contains(event->pos())) + event->accept(); +} + +void ClickableLabel::mouseReleaseEvent(QMouseEvent *event) +{ + if (rect().contains(event->pos())) { + event->accept(); + Q_EMIT clicked(); + } +} diff --git a/src/Authoring/Qt3DStudio/Controls/ClickableLabel.h b/src/Authoring/Qt3DStudio/Controls/ClickableLabel.h new file mode 100644 index 00000000..591baa9a --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/ClickableLabel.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef CLICKABLELABEL_H +#define CLICKABLELABEL_H + +#include <QLabel> + +class ClickableLabel : public QLabel +{ + Q_OBJECT +public: + ClickableLabel(QWidget *pr = nullptr); + +Q_SIGNALS: + void clicked(); + +protected: + void mouseReleaseEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; +}; + +#endif // CLICKABLELABEL_H 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(); +} diff --git a/src/Authoring/Qt3DStudio/Controls/Control.h b/src/Authoring/Qt3DStudio/Controls/Control.h new file mode 100644 index 00000000..570005fa --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/Control.h @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef INCLUDED_CONTROL_H +#define INCLUDED_CONTROL_H 1 + +#pragma once + +#include "Pt.h" +#include "Rct.h" +#include "SafeArray.h" +#include "GenericFunctor.h" +#include "PlatformTypes.h" + +#include "DropTarget.h" +#include "ControlGraphIterators.h" +#include "DocumentEditorEnumerations.h" + +class CRenderer; +class CContextMenu; +class Qt3DSFile; +class IDragable; +class CAsset; +class CStudioApp; + +QT_FORWARD_DECLARE_CLASS(QMenu) + +namespace Q3DStudio { +namespace Control { + class CControlData; + using std::pair; + using std::make_pair; + using std::vector; +} +} + +// this functor is used to tell when a child loses focus the boolean should be true if the child +// gains focus +GENERIC_FUNCTOR_1(CChildFocusListener, OnChildFocusChange, bool) + +#ifdef _WIN32 +typedef HWND TPlatformView; +#else +typedef void *TPlatformView; +#endif + +class CControlWindowListener +{ +public: + virtual void OnControlInvalidated() = 0; + virtual long DoPopup(QMenu *inMenu, CPt inLocation) = 0; + virtual CPt ClientToScreen(CPt inPoint) = 0; + virtual CPt ScreenToClient(CPt inPoint) = 0; + virtual TPlatformView GetPlatformView() = 0; + virtual void SetIsDragging(bool inIsDragging) = 0; + virtual void ShowTooltips(CPt inLocation, const QString &inText) = 0; + virtual void HideTooltips() = 0; + virtual void DoStartDrag(IDragable *inDragable) = 0; + virtual void DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileList) = 0; + virtual void ShowMoveableWindow(CPt inLocation, const Q3DStudio::CString &inText, + CRct inBoundingRct) = 0; + virtual void HideMoveableWindow() = 0; +}; + +namespace Q3DStudio { +namespace Control { + class CControlData; +} +} + +class CControl +{ + + CControl(CControl &inOther); + CControl &operator=(CControl &inOther); + +public: + CControl(); + virtual ~CControl(); + + DEFINE_OBJECT_COUNTER(CControl) + + CControl *GetParent(); + const CControl *GetParent() const; + + virtual void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation = false); + virtual void Draw(CRenderer *inRenderer); + virtual void NotifyNotInClipRect(); + + virtual CPt GetPosition() const; + virtual void SetPosition(CPt inPosition); + void SetPosition(long inX, long inY); + + virtual CPt GetSize() const; + virtual void SetSize(CPt inSize); + virtual void SetSize(long inWidth, long inHeight); + virtual void OnSizeChanged(CPt inSize); + + virtual CPt GetMinimumSize(); + virtual void SetMinimumSize(CPt inSize); + + virtual CPt GetMaximumSize(); + virtual void SetMaximumSize(CPt inSize); + + virtual CPt GetPreferredSize(); + virtual void SetPreferredSize(CPt inSize); + + virtual void SetAbsoluteSize(CPt inSize); + + virtual void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags); + virtual void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags); + virtual void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags); + virtual bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags); + virtual bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags); + virtual void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags); + virtual void OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags); + virtual void OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags); + virtual bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags); + virtual bool OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags); + /** + Note that just overriding this isn't enough, you need to call + m_ControlData->SetMouseWheelEnabled( true ) + in order to receive mouse wheel events + */ + virtual bool OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags); + virtual bool OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags); + virtual bool OnKeyUp(unsigned int inChar, Qt::KeyboardModifiers inFlags); + virtual bool OnChar(const QString &inChar, Qt::KeyboardModifiers inFlags); + virtual void OnLoseFocus(); + virtual void OnGainFocus(); + virtual bool CanGainFocus(); + virtual bool IsInFocus(); + void OnTab(); + void OnReverseTab(); + void SetFocusToFirstAvailable(); + void SetFocusToLastAvailable(); + + virtual CDropTarget *FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags, + EStudioObjectType objectType, + Q3DStudio::DocumentEditorFileType::Enum fileType); + + virtual void AddChild(CControl *inControl, CControl *inInsertBefore = NULL); + virtual void RemoveChild(CControl *inControl); + virtual void RemoveAllChildren(); + virtual long GetChildIndex(CControl *inChildControl); + virtual CControl *FindChildByName(const Q3DStudio::CString &inName); + CControl *FocusedChild(); + + virtual bool IsMouseOver() const; + + virtual bool HitTest(const CPt &inPoint) const; + virtual bool IsInRect(const CRct &inRect) const; + + virtual void Invalidate(bool inInvalidate = true); + virtual void InvalidateRect(const CRct &inRect); + virtual bool IsInvalidated() const; + + virtual void OnChildInvalidated(); + virtual bool IsChildInvalidated() const; + + virtual void SetVisible(bool inIsVisible); + virtual bool IsVisible() const; + virtual void OnVisibleStateChange(bool inIsVisible); + virtual void OnParentVisibleStateChanged(bool inIsVisible); + + virtual void SetParentEnabled(bool inParentEnabled); + virtual void SetEnabled(bool inIsEnabled); + virtual bool IsEnabled() const; + bool GetEnabledFlag(); + void SetEnabledFlag(bool inIsEnabled); + + virtual void OnChildSizeChanged(CControl *inChild); + virtual void ResetMinMaxPref(){} + + void SetName(const QString &inName); + QString GetName(); + + virtual void BeginDrawChildren(CRenderer *inRenderer); + + virtual long DoPopup(QMenu *inContextMenu, CPt inPoint); + virtual void RemoveUberControl(CControl *inControl); + virtual void OffsetPosition(CPt inOffset); + + virtual CPt GetGlobalPosition(CPt inChildPoint) const; + virtual Qt3DSRenderDevice GetPlatformDevice(); + bool IsChildPlatformDevice(Qt3DSRenderDevice inDevice); + virtual void ShowMoveableWindow(CPt inLocation, const Q3DStudio::CString &inText, + CRct inBoundingRct); + virtual void HideMoveableWindow(); + CControl *GetFirstChild(); + bool HasFocus(CControl *inControl); + virtual void GrabFocus(CControl *inControl); + + virtual CPt ClientToScreen(CPt inPoint); + virtual CPt ScreenToClient(CPt inPoint); + + void AddFocusListener(CChildFocusListener *inListener); + void RemoveFocusListener(CChildFocusListener *inListener); + void FireFocusEvent(bool inStatus); + + virtual void EnsureVisible(CRct inRect); + void EnsureVisible(); + + virtual void OnParentChanged(CControl *inNewParent); + + virtual void MarkChildrenNeedLayout(); + virtual void MarkNeedsLayout(); + // Tell our parent that we (and all our siblings) need to be + // laid out. + virtual void NotifyParentNeedsLayout(); + // Tell this control that one of its children need to be layed out. + virtual void SetLayout(CPt inSize, CPt inPosition); + virtual void LayoutChildren(); + virtual void EnsureLayout(); + + virtual void ChildrenChanged(); + + Q3DStudio::Control::ControlGraph::SIterator GetChildren(); + Q3DStudio::Control::ControlGraph::SReverseIterator GetReverseChildren(); + +protected: ///< Current size of this control + // Member Functions + + long GetChildCount(); + TPlatformView GetPlatformView(); + bool IsMouseDown(); + void setCursorIfNotSet(long cursor); + void resetCursor(); + QCursor getCursor() const; + + std::shared_ptr<Q3DStudio::Control::CControlData> m_ControlData; + long m_cursorSet; +}; +#endif // INCLUDED_CONTROL_H diff --git a/src/Authoring/Qt3DStudio/Controls/ControlData.cpp b/src/Authoring/Qt3DStudio/Controls/ControlData.cpp new file mode 100644 index 00000000..eb3bf0f2 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/ControlData.cpp @@ -0,0 +1,765 @@ +/**************************************************************************** +** +** 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 "ControlData.h" +#include "ControlGraph.h" +#include "Control.h" + +#include <QtWidgets/qmenu.h> + +using namespace Q3DStudio::Control; +using Q3DStudio::CString; + +CControlData::CControlData(CControl &inControl) + : m_Control(&inControl) + , m_MinSize(0, 0) + , m_MaxSize(LONG_MAX, LONG_MAX) + , m_PrefSize(10, 10) + , m_IsMouseOver(false) + , m_IsInvalidated(true) + , m_IsChildInvalidated(false) + , m_IsVisible(true) + , m_IsEnabled(true) + , m_IsParentEnabled(true) + , m_HasFocus(false) + , m_IsMouseDown(false) + , m_ShowTooltips(false) + , m_NeedsLayout(true) + , m_ChildrenNeedLayout(true) + , m_WindowListener(NULL) +{ +} + +CControlData::~CControlData() +{ +} + +void CControlData::ReleaseControl() +{ + if (m_Control) { + ControlGraph::RemoveNode(*m_Control); + m_Control = nullptr; + } +} + +std::shared_ptr<CControlData> CControlData::CreateControlData(CControl &inControl) +{ + std::shared_ptr<CControlData> retval = + std::make_shared<CControlData>(std::ref(inControl)); + ControlGraph::AddNode(retval); + return retval; +} + +CControl *CControlData::GetControl() +{ + return m_Control; +} + +CControl *CControlData::GetParent() +{ + + if (m_Control) { + std::shared_ptr<CControlData> theParent(ControlGraph::GetParent(*m_Control)); + if (theParent) + return theParent->GetControl(); + } + return nullptr; +} + +void CControlData::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation) +{ + if (m_Control) + m_Control->OnDraw(inRenderer, inDirtyRect, inIgnoreValidation); +} + +void CControlData::Draw(CRenderer *inRenderer) +{ + if (m_Control) + m_Control->Draw(inRenderer); +} + +void CControlData::NotifyNotInClipRect() +{ + if (m_Control) + m_Control->NotifyNotInClipRect(); +} + +CPt CControlData::GetPosition() const +{ + if (m_Control) + return m_Control->GetPosition(); + return CPt(); +} + +void CControlData::SetPosition(CPt inPosition) +{ + if (m_Control) + m_Control->SetPosition(inPosition); +} + +void CControlData::SetPosition(long inX, long inY) +{ + SetPosition(CPt(inX, inY)); +} + +CPt CControlData::GetSize() const +{ + if (m_Control) + return m_Control->GetSize(); + return CPt(); +} + +void CControlData::SetSize(CPt inSize) +{ + if (m_Control) + m_Control->SetSize(inSize); +} + +void CControlData::SetSize(long inWidth, long inHeight) +{ + SetSize(CPt(inWidth, inHeight)); +} + +CPt CControlData::GetMinimumSize() +{ + if (m_Control) + return m_Control->GetMinimumSize(); + return CPt(); +} + +void CControlData::SetMinimumSize(CPt inSize) +{ + if (m_Control) + m_Control->SetMinimumSize(inSize); +} + +CPt CControlData::GetMaximumSize() +{ + if (m_Control) + return m_Control->GetMaximumSize(); + return CPt(); +} + +void CControlData::SetMaximumSize(CPt inSize) +{ + if (m_Control) + m_Control->SetMaximumSize(inSize); +} + +CPt CControlData::GetPreferredSize() +{ + if (m_Control) + return m_Control->GetPreferredSize(); + return CPt(); +} + +void CControlData::SetPreferredSize(CPt inSize) +{ + if (m_Control) + return m_Control->SetPreferredSize(inSize); +} + +void CControlData::SetAbsoluteSize(CPt inSize) +{ + if (m_Control) + m_Control->SetAbsoluteSize(inSize); +} + +void CControlData::SetMouseDown(bool inMouseDown) +{ + m_IsMouseDown = inMouseDown; +} + +void CControlData::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + m_Control->OnMouseMove(inPoint, inFlags); +} + +void CControlData::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + m_Control->OnMouseOver(inPoint, inFlags); +} + +void CControlData::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + m_Control->OnMouseOut(inPoint, inFlags); +} + +bool CControlData::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + return m_Control->OnMouseDown(inPoint, inFlags); + return false; +} + +bool CControlData::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + return m_Control->OnMouseRDown(inPoint, inFlags); + return false; +} + +void CControlData::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + m_Control->OnMouseUp(inPoint, inFlags); +} + +void CControlData::OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + m_Control->OnMouseRUp(inPoint, inFlags); +} + +void CControlData::OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + m_Control->OnMouseClick(inPoint, inFlags); +} + +bool CControlData::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + return m_Control->OnMouseDoubleClick(inPoint, inFlags); + return false; +} + +bool CControlData::OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + return m_Control->OnMouseHover(inPoint, inFlags); + return false; +} + +bool CControlData::OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + return m_Control->OnMouseWheel(inPoint, inAmount, inFlags); + return false; +} + +bool CControlData::OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + return m_Control->OnKeyDown(inChar, inFlags); + return false; +} + +bool CControlData::OnKeyUp(unsigned int inChar, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + return m_Control->OnKeyUp(inChar, inFlags); + return false; +} + +bool CControlData::OnChar(const QString &inChar, Qt::KeyboardModifiers inFlags) +{ + if (m_Control) + return m_Control->OnChar(inChar, inFlags); + return false; +} + +void CControlData::OnLoseFocus() +{ + if (m_Control) + m_Control->OnLoseFocus(); +} + +void CControlData::OnGainFocus() +{ + if (m_Control) + m_Control->OnGainFocus(); +} + +bool CControlData::CanGainFocus() +{ + if (m_Control) + return m_Control->CanGainFocus(); + return false; +} + +bool CControlData::IsInFocus() +{ + if (m_Control) + return m_Control->IsInFocus(); + return false; +} + +void CControlData::OnTab() +{ + if (m_Control) + m_Control->OnTab(); +} + +void CControlData::OnReverseTab() +{ + if (m_Control) + m_Control->OnReverseTab(); +} + +void CControlData::SetFocusToFirstAvailable() +{ + if (m_Control) + m_Control->SetFocusToFirstAvailable(); +} + +void CControlData::SetFocusToLastAvailable() +{ + if (m_Control) + m_Control->SetFocusToLastAvailable(); +} + +void CControlData::ChildrenChanged() +{ + if (m_Control) + m_Control->ChildrenChanged(); +} + +void CControlData::SetMouseWheelEnabled(bool inEnabled) +{ + if (m_Control == nullptr) + return; + + if (m_MouseWheelState.m_Enabled != inEnabled) { + m_MouseWheelState.m_Enabled = inEnabled; + ControlEventState::Enum theNewState; + if (m_MouseWheelState.m_Enabled == false) + theNewState = ControlEventState::Unknown; + else + theNewState = ControlEventState::Listening; + SetMouseWheelEventState(theNewState); + } +} + +void CControlData::OnHierarchyChanged() +{ + ControlEventState::Enum theNewState; + if (m_MouseWheelState.m_Enabled) + theNewState = ControlEventState::Listening; + else + theNewState = ControlEventState::Unknown; + SetMouseWheelEventState(theNewState); +} + +/** + Look at hierarchy information to figure out which events should + trickle down the tree to here (or deeper). Recursive call that + may take some time to complete. +*/ +void CControlData::UpdateMouseWheelEventState() +{ + if (m_Control == nullptr) + return; + ControlEventState::Enum theNewState(ControlEventState::Ignoring); + if (m_MouseWheelState.m_Enabled == true) + theNewState = ControlEventState::Listening; + else if (m_MouseWheelState.m_EventState == ControlEventState::Unknown && m_Control != nullptr) { + for (ControlGraph::SIterator theIter(ControlGraph::GetChildren(*m_Control)); + theIter.IsDone() == false; ++theIter) { + if (theIter->GetMouseWheelEventState() == ControlEventState::Listening) { + theNewState = ControlEventState::Listening; + break; + } + } + } + SetMouseWheelEventState(theNewState); +} + +/** + Update our mouse wheel state and notify our parent if necessary. Note that we + can't set our event state to ignoring without checking all of our children + so the result of this function is that our event state is either listening + or unknown. +*/ +void CControlData::SetMouseWheelEventState(ControlEventState::Enum inNewState) +{ + if (m_MouseWheelState.m_EventState != inNewState) { + m_MouseWheelState.m_EventState = inNewState; + std::shared_ptr<CControlData> theParent = ControlGraph::GetParent(*m_Control); + if (theParent) + theParent->ChildMouseWheelEventStateChanged(m_MouseWheelState.m_EventState); + } +} + +/** + When a given child notifies us that its event state has changed then we can update + our state. We can't set the state to ignoring unless we *know* all of our + children are ignoring mouse wheel. + */ +void CControlData::ChildMouseWheelEventStateChanged(ControlEventState::Enum inNewState) +{ + ControlEventState::Enum theNewState; + if (inNewState == ControlEventState::Listening || m_MouseWheelState.m_Enabled == true) + theNewState = ControlEventState::Listening; + else + theNewState = ControlEventState::Unknown; + + SetMouseWheelEventState(theNewState); +} + +ControlEventState::Enum CControlData::GetMouseWheelEventState() +{ + if (m_MouseWheelState.m_EventState == ControlEventState::Unknown) + UpdateMouseWheelEventState(); + assert(m_MouseWheelState.m_EventState != ControlEventState::Unknown); + return m_MouseWheelState.m_EventState; +} + +CDropTarget *CControlData::FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags, + EStudioObjectType objectType, + Q3DStudio::DocumentEditorFileType::Enum fileType) +{ + if (m_Control) + return m_Control->FindDropCandidate(inMousePoint, inFlags, objectType, fileType); + return nullptr; +} + +void CControlData::AddChild(CControl *inControl, CControl *inInsertBefore) +{ + if (m_Control) + m_Control->AddChild(inControl, inInsertBefore); +} + +void CControlData::RemoveChild(CControl *inControl) +{ + if (m_Control) + m_Control->RemoveChild(inControl); +} + +void CControlData::RemoveAllChildren() +{ + if (m_Control) + m_Control->RemoveAllChildren(); +} + +long CControlData::GetChildIndex(CControl *inChildControl) +{ + if (m_Control) + return m_Control->GetChildIndex(inChildControl); + return 0; +} + +CControl *CControlData::FindChildByName(const Q3DStudio::CString &inName) +{ + if (m_Control) + m_Control->FindChildByName(inName); + return nullptr; +} + +bool CControlData::IsMouseOver() const +{ + if (m_Control) + return m_Control->IsMouseOver(); + return false; +} + +bool CControlData::HitTest(const CPt &inPoint) const +{ + if (m_Control) + return m_Control->HitTest(inPoint); + return false; +} +bool CControlData::IsInRect(const CRct &inRect) const +{ + if (m_Control) + return m_Control->IsInRect(inRect); + return false; +} + +void CControlData::Invalidate(bool inInvalidate) +{ + if (m_Control) + m_Control->Invalidate(inInvalidate); +} + +void CControlData::InvalidateRect(const CRct &inRect) +{ + if (m_Control) + m_Control->InvalidateRect(inRect); +} + +bool CControlData::IsInvalidated() const +{ + return m_IsInvalidated; +} + +void CControlData::OnChildInvalidated() +{ + if (m_Control) + m_Control->OnChildInvalidated(); +} + +bool CControlData::IsChildInvalidated() const +{ + if (m_Control) + return m_Control->IsChildInvalidated(); + return false; +} + +void CControlData::SetVisible(bool inIsVisible) +{ + if (m_Control) + m_Control->SetVisible(inIsVisible); +} + +bool CControlData::IsVisible() const +{ + if (m_Control) + return m_Control->IsVisible(); + return false; +} + +void CControlData::OnVisibleStateChange(bool inIsVisible) +{ + if (m_Control) + m_Control->OnVisibleStateChange(inIsVisible); +} + +void CControlData::OnParentVisibleStateChanged(bool inIsVisible) +{ + if (m_Control) + m_Control->OnParentVisibleStateChanged(inIsVisible); +} + +void CControlData::SetParentEnabled(bool inParentEnabled) +{ + if (m_Control) + m_Control->SetParentEnabled(inParentEnabled); +} + +void CControlData::SetEnabled(bool inIsEnabled) +{ + if (m_Control) + m_Control->SetEnabled(inIsEnabled); +} + +bool CControlData::IsEnabled() const +{ + return m_IsEnabled && m_IsParentEnabled; +} + +bool CControlData::GetEnabledFlag() +{ + return m_IsEnabled; +} + +void CControlData::SetEnabledFlag(bool inIsEnabled) +{ + m_IsEnabled = inIsEnabled; +} + +void CControlData::OnChildSizeChanged(CControl *inChild) +{ + if (m_Control) + m_Control->OnChildSizeChanged(inChild); +} + +void CControlData::ResetMinMaxPref() +{ + if (m_Control) + m_Control->ResetMinMaxPref(); +} + +void CControlData::SetName(CString inName) +{ + m_ControlName = inName; +} + +CString CControlData::GetName() +{ + return m_ControlName; +} + +void CControlData::BeginDrawChildren(CRenderer *inRenderer) +{ + if (m_Control) + m_Control->BeginDrawChildren(inRenderer); +} + +long CControlData::DoPopup(QMenu *inContextMenu, CPt inPoint) +{ + if (m_Control) + return m_Control->DoPopup(inContextMenu, inPoint); + return 0; +} + +void CControlData::RemoveUberControl(CControl *inControl) +{ + if (m_Control) + m_Control->RemoveUberControl(inControl); +} + +void CControlData::OffsetPosition(CPt inOffset) +{ + if (m_Control) + m_Control->OffsetPosition(inOffset); +} + +CPt CControlData::GetGlobalPosition(CPt inChildPoint) const +{ + if (m_Control) + return m_Control->GetGlobalPosition(inChildPoint); + return CPt(); +} + +Qt3DSRenderDevice CControlData::GetPlatformDevice() +{ + if (m_Control) + return m_Control->GetPlatformDevice(); + return nullptr; +} + +bool CControlData::IsChildPlatformDevice(Qt3DSRenderDevice inDevice) +{ + if (m_Control) + return m_Control->IsChildPlatformDevice(inDevice); + return false; +} + +void CControlData::ShowMoveableWindow(CPt inLocation, Q3DStudio::CString inText, CRct inBoundingRct) +{ + if (m_Control) + m_Control->ShowMoveableWindow(inLocation, inText, inBoundingRct); +} + +void CControlData::HideMoveableWindow() +{ + if (m_Control) + m_Control->HideMoveableWindow(); +} + +CControl *CControlData::GetFirstChild() +{ + if (m_Control) + m_Control->GetFirstChild(); + return nullptr; +} + +bool CControlData::HasFocus(CControl *inControl) +{ + if (m_Control) + return m_Control->HasFocus(inControl); + return false; +} + +void CControlData::GrabFocus(CControl *inControl) +{ + if (m_Control) + m_Control->GrabFocus(inControl); +} + +CPt CControlData::ClientToScreen(CPt inPoint) +{ + if (m_Control) + return m_Control->ClientToScreen(inPoint); + return CPt(); +} + +CPt CControlData::ScreenToClient(CPt inPoint) +{ + if (m_Control) + return m_Control->ScreenToClient(inPoint); + return CPt(); +} + +void CControlData::AddFocusListener(CChildFocusListener *inListener) +{ + if (m_Control) + m_Control->AddFocusListener(inListener); +} + +void CControlData::RemoveFocusListener(CChildFocusListener *inListener) +{ + if (m_Control) + m_Control->RemoveFocusListener(inListener); +} + +void CControlData::FireFocusEvent(bool inStatus) +{ + if (m_Control) + m_Control->FireFocusEvent(inStatus); +} + +void CControlData::EnsureVisible(CRct inRect) +{ + if (m_Control) + m_Control->EnsureVisible(inRect); +} + +void CControlData::EnsureVisible() +{ + if (m_Control) + m_Control->EnsureVisible(); +} + +void CControlData::OnParentChanged(CControl *inControl) +{ + if (m_Control) + m_Control->OnParentChanged(inControl); +} + +void CControlData::NotifyParentNeedsLayout() +{ + if (m_Control) + m_Control->NotifyParentNeedsLayout(); +} + +void CControlData::MarkChildrenNeedLayout() +{ + if (m_Control) + m_Control->MarkChildrenNeedLayout(); +} + +void CControlData::MarkNeedsLayout() +{ + if (m_Control) + m_Control->MarkNeedsLayout(); +} + +// Tell this control that one of its children need to be layed out. +void CControlData::SetLayout(CPt inSize, CPt inPosition) +{ + if (m_Control) + m_Control->SetLayout(inSize, inPosition); +} + +void CControlData::LayoutChildren() +{ + if (m_Control) + m_Control->LayoutChildren(); +} + +void CControlData::EnsureLayout() +{ + if (m_Control) + m_Control->EnsureLayout(); +} diff --git a/src/Authoring/Qt3DStudio/Controls/ControlData.h b/src/Authoring/Qt3DStudio/Controls/ControlData.h new file mode 100644 index 00000000..d2f6870e --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/ControlData.h @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#ifndef CONTROLDATAH +#define CONTROLDATAH + +#include "DropTarget.h" +#include "Pt.h" +#include "Rct.h" +#include "Multicaster.h" +#include "Qt3DSString.h" +#include "DocumentEditorEnumerations.h" + +class CControl; +class CControlWindowListener; +class CChildFocusListener; +class CRenderer; + +QT_FORWARD_DECLARE_CLASS(QMenu) + +namespace Q3DStudio { +namespace Control { + +using namespace std; + +struct ControlEventState +{ + enum Enum { + Unknown = 0, + Listening, + Ignoring, + }; +}; + +struct SControlEventData +{ + bool m_Enabled; + ControlEventState::Enum m_EventState; + SControlEventData() + : m_Enabled(false) + , m_EventState(ControlEventState::Ignoring) + { + } +}; + +// Smart pointer object that referees access to a control. +class CControlData +{ + Q_DISABLE_COPY(CControlData) +public: + friend class ::CControl; + friend class std::shared_ptr<CControlData>; + +private: + // The physical address of the control is the key in the graph + // that points to this data item. + CControl *m_Control; + + CPt m_Size; ///< Current size of this control + + CPt m_Position; ///< Position of this control, relative to the parent + CPt m_MinSize; ///< Minimum allowed size of this control + CPt m_MaxSize; ///< Maximum allowed size of this contrl + CPt m_PrefSize; ///< Preferred size of this control + + bool m_IsMouseOver; ///< True if the mouse is over this control + bool m_IsInvalidated; ///< True if this control needs to be redrawn + bool m_IsChildInvalidated; ///< True if a child of this control is invalidated. + bool m_IsVisible; ///< True if this control is to be visible + bool m_IsEnabled; ///< True if this control is enabled. + bool m_IsParentEnabled; + bool m_HasFocus; + bool m_IsMouseDown; + bool m_ShowTooltips; ///< Specifies whether or not tooltips should be shown + bool m_NeedsLayout; ///< True if this control needs to be layed out. + bool m_ChildrenNeedLayout; ///< True if my children need layout + SControlEventData + m_MouseWheelState; ///< Tracks whether this object cares about mouse wheel. + + QString m_TooltipText; ///< Text to be displayed for tooltips + + std::shared_ptr<CControlData> m_Focus; ///< Child control that has the focus. + std::shared_ptr<CControlData> m_MouseFocus; ///< Child control that got the mouse down. + CControlWindowListener + *m_WindowListener; ///< External listener for when this control is invalidated. + + Q3DStudio::CString m_ControlName; + CMulticaster<CChildFocusListener *> m_FocusListeners; ///< Used for focus changes + +public: + CControlData(CControl &inControl); + ~CControlData(); + + CControl *GetControl(); + + CControl *GetParent(); + + void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation = false); + void Draw(CRenderer *inRenderer); + void NotifyNotInClipRect(); + + CPt GetPosition() const; + void SetPosition(CPt inPosition); + void SetPosition(long inX, long inY); + + CPt GetSize() const; + void SetSize(CPt inSize); + void SetSize(long inWidth, long inHeight); + + CPt GetMinimumSize(); + void SetMinimumSize(CPt inSize); + + CPt GetMaximumSize(); + void SetMaximumSize(CPt inSize); + + CPt GetPreferredSize(); + void SetPreferredSize(CPt inSize); + + void SetAbsoluteSize(CPt inSize); + + void SetMouseDown(bool inMouseDown); + void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags); + void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags); + void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags); + bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags); + bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags); + void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags); + void OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags); + void OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags); + bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags); + bool OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags); + bool OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags); + bool OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags); + bool OnKeyUp(unsigned int inChar, Qt::KeyboardModifiers inFlags); + bool OnChar(const QString &inChar, Qt::KeyboardModifiers inFlags); + void OnLoseFocus(); + void OnGainFocus(); + bool CanGainFocus(); + bool IsInFocus(); + void OnTab(); + void OnReverseTab(); + void SetFocusToFirstAvailable(); + void SetFocusToLastAvailable(); + void ChildrenChanged(); + + // Mouse wheel event state. + void SetMouseWheelEnabled(bool inEnabled); + /** + Callback so we can update any event states to unknown + from ignoring forcing a refresh of hierarchy event information + the next time the event state is queried. + */ + void OnHierarchyChanged(); + /** + If our event state is unknown, force resolution by asking children what their + event state is. Else return our event state. + */ + ControlEventState::Enum GetMouseWheelEventState(); + +protected: + void UpdateMouseWheelEventState(); + void ChildMouseWheelEventStateChanged(ControlEventState::Enum inNewState); + void SetMouseWheelEventState(ControlEventState::Enum inNewState); + +public: + CDropTarget *FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags, + EStudioObjectType objectType, + Q3DStudio::DocumentEditorFileType::Enum fileType); + + void AddChild(CControl *inControl, CControl *inInsertBefore = NULL); + void RemoveChild(CControl *inControl); + void RemoveAllChildren(); + long GetChildIndex(CControl *inChildControl); + CControl *FindChildByName(const Q3DStudio::CString &inName); + + bool IsMouseOver() const; + + bool HitTest(const CPt &inPoint) const; + bool IsInRect(const CRct &inRect) const; + + void Invalidate(bool inInvalidate = true); + void InvalidateRect(const CRct &inRect); + bool IsInvalidated() const; + + void OnChildInvalidated(); + bool IsChildInvalidated() const; + + void SetVisible(bool inIsVisible); + bool IsVisible() const; + void OnVisibleStateChange(bool inIsVisible); + void OnParentVisibleStateChanged(bool inIsVisible); + + void SetParentEnabled(bool inParentEnabled); + void SetEnabled(bool inIsEnabled); + bool IsEnabled() const; + bool GetEnabledFlag(); + void SetEnabledFlag(bool inIsEnabled); + + void OnChildSizeChanged(CControl *inChild); + void ResetMinMaxPref(); + + void SetName(Q3DStudio::CString inName); + Q3DStudio::CString GetName(); + + void BeginDrawChildren(CRenderer *inRenderer); + + long DoPopup(QMenu *inContextMenu, CPt inPoint); + void RemoveUberControl(CControl *inControl); + void OffsetPosition(CPt inOffset); + + CPt GetGlobalPosition(CPt inChildPoint) const; + Qt3DSRenderDevice GetPlatformDevice(); + bool IsChildPlatformDevice(Qt3DSRenderDevice inDevice); + void ShowMoveableWindow(CPt inLocation, Q3DStudio::CString inText, CRct inBoundingRct); + void HideMoveableWindow(); + CControl *GetFirstChild(); + bool HasFocus(CControl *inControl); + void GrabFocus(CControl *inControl); + + CPt ClientToScreen(CPt inPoint); + CPt ScreenToClient(CPt inPoint); + + void AddFocusListener(CChildFocusListener *inListener); + void RemoveFocusListener(CChildFocusListener *inListener); + void FireFocusEvent(bool inStatus); + + void EnsureVisible(CRct inRect); + void EnsureVisible(); + + void OnParentChanged(CControl *inControl); + + void NotifyParentNeedsLayout(); + void MarkChildrenNeedLayout(); + void MarkNeedsLayout(); + // Tell this control that one of its children need to be layed out. + void SetLayout(CPt inSize, CPt inPosition); + void LayoutChildren(); + void EnsureLayout(); + +private: + static std::shared_ptr<CControlData> CreateControlData(CControl &inControl); + void ReleaseControl(); // set the control to null, remove our graph representation +}; +} +} + +#endif diff --git a/src/Authoring/Qt3DStudio/Controls/ControlGraph.cpp b/src/Authoring/Qt3DStudio/Controls/ControlGraph.cpp new file mode 100644 index 00000000..34b59b7c --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/ControlGraph.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** 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 "ControlGraph.h" +#include "GraphImpl.h" +#include "foundation/Qt3DSAssert.h" +#include "Control.h" +#include "ControlData.h" + +using namespace std; +using namespace Q3DStudio; +using namespace Q3DStudio::Graph; +using namespace Q3DStudio::Control; +using Q3DStudio::CString; + +namespace { +typedef SGraphImpl<CControl *, std::shared_ptr<CControlData>> TGraphType; + +TGraphType g_ControlGraph; +} + +namespace Q3DStudio { +namespace Control { + namespace ControlGraph { + + void AddNode(std::shared_ptr<CControlData> inData) + { + g_ControlGraph.AddRoot(inData->GetControl()); + g_ControlGraph.SetData(inData->GetControl(), inData); + } + + void RemoveNode(CControl &inData) { g_ControlGraph.RemoveChild(&inData, true); } + + void AddChild(CControl &inParent, CControl &inChild, CControl *inNextSibling) + { + std::shared_ptr<CControlData> oldParent = GetParent(inChild); + TGraphType::TNodePtr theParent(g_ControlGraph.GetImpl(&inParent)); + + // It actually happens that sometimes inChild gets added with a sibling + // but the sibling hasn't actually been added yet. + if (inNextSibling != nullptr) { + TGraphType::TNodePtr theSibling(g_ControlGraph.GetImpl(inNextSibling)); + if (theSibling->m_Parent == theParent) + g_ControlGraph.MoveBefore(&inChild, inNextSibling); + else + g_ControlGraph.AddChild(&inParent, &inChild, SGraphPosition::SEnd()); + } else + g_ControlGraph.AddChild(&inParent, &inChild, SGraphPosition::SEnd()); + + if (!oldParent || oldParent->GetControl() != &inParent) { + inChild.OnParentChanged(&inParent); + if (oldParent) + oldParent->ChildrenChanged(); + } + inChild.Invalidate(); + inParent.ChildrenChanged(); + } + // inParent is supplied for error checking purposes. + void RemoveChild(CControl &inParent, CControl &inChild) + { + inChild.OnParentChanged(nullptr); + g_ControlGraph.RemoveChild(&inParent, &inChild, false); + inChild.Invalidate(); + inParent.ChildrenChanged(); + } + void RemoveAllChildren(CControl &inParent) + { + TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inParent)); + if (theNode == nullptr) + return; + while (theNode->m_Children.empty() == false) { + theNode->m_Children.back()->m_Data->OnParentChanged(nullptr); + theNode->m_Children.back()->m_Data->NotifyParentNeedsLayout(); + g_ControlGraph.RemoveChild(&inParent, theNode->m_Children.back()->m_GraphableID, + false); + } + inParent.ChildrenChanged(); + } + void MoveTo(CControl &inParent, CControl &inChild, const Graph::SGraphPosition &inPosition) + { + std::shared_ptr<CControlData> theOldParent(GetParent(inChild)); + g_ControlGraph.MoveTo(&inParent, &inChild, inPosition); + if (!theOldParent || theOldParent->GetControl() != &inParent) + inChild.OnParentChanged(&inParent); + inChild.NotifyParentNeedsLayout(); + inParent.MarkChildrenNeedLayout(); + if (theOldParent) { + theOldParent->ChildrenChanged(); + } + inParent.ChildrenChanged(); + inChild.Invalidate(); + } + long GetNumChildren(CControl &inControl) + { + return g_ControlGraph.GetChildCount(&inControl); + } + + long GetChildIndex(CControl &inParent, CControl &inChild) + { + QT3DS_ASSERT(GetParent(inChild)->GetControl() == &inParent); + + SGraphPosition thePos = g_ControlGraph.GetNodePosition(&inChild); + return thePos.GetIndex(); + } + + std::shared_ptr<CControlData> GetChild(CControl &inParent, long inIndex) + { + TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inParent)); + if (theNode && inIndex < (long)theNode->m_Children.size() && inIndex > -1) + return theNode->m_Children[inIndex]->m_Data; + return std::shared_ptr<CControlData>(); + } + + std::shared_ptr<CControlData> GetParent(CControl &inControl) + { + TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inControl)); + if (theNode && theNode->m_Parent) + return theNode->m_Parent->m_Data; + return std::shared_ptr<CControlData>(); + } + + // Dummy struct to hide graph implementation + struct SDummyGraphNode : public TGraphType::TNodeType + { + SDummyGraphNode(CControl *const &inIdentifier, + const std::shared_ptr<CControlData> &inNodeData = + std::shared_ptr<CControlData>()) + : TGraphType::TNodeType(inIdentifier, inNodeData) + { + } + }; + + std::shared_ptr<CControlData> SIteratorBase::GetCurrent() + { + if (m_Children && m_Index < (long)m_Children->size()) + return (*m_Children)[m_Index]->m_Data; + + QT3DS_ASSERT(false); + return std::shared_ptr<CControlData>(); + } + + SReverseIterator GetRChildren(CControl &inControl) + { + TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inControl)); + if (theNode) + return SReverseIterator( + theNode->m_Data, + reinterpret_cast<vector<SDummyGraphNode *> &>(theNode->m_Children)); + return SReverseIterator(); + } + + SIterator GetChildren(CControl &inControl) + { + TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inControl)); + if (theNode) + return SIterator(theNode->m_Data, reinterpret_cast<vector<SDummyGraphNode *> &>( + theNode->m_Children)); + return SIterator(); + } + } +} +} diff --git a/src/Authoring/Qt3DStudio/Controls/ControlGraph.h b/src/Authoring/Qt3DStudio/Controls/ControlGraph.h new file mode 100644 index 00000000..0dfe2b30 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/ControlGraph.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#ifndef CONTROLGRAPHH +#define CONTROLGRAPHH +#include "Multicaster.h" +#include "ControlGraphIterators.h" + +class CControl; +class CControlWindowListener; +class CChildFocusListener; +class CRenderer; + +namespace Q3DStudio { +namespace Graph { + + struct SGraphPosition; // GraphPosition.h" +} + +namespace Control { + class ControlData; + + namespace ControlGraph { + void AddNode(std::shared_ptr<CControlData> inData); + void RemoveNode(CControl &inData); + void AddChild(CControl &inParent, CControl &inChild, CControl *inNextSibling); + // inParent is supplied for error checking purposes. + void RemoveChild(CControl &inParent, CControl &inChild); + void RemoveAllChildren(CControl &inParent); + void MoveTo(CControl &inParent, CControl &inChild, const Graph::SGraphPosition &inPosition); + long GetNumChildren(CControl &inControl); + long GetChildIndex(CControl &inParent, CControl &inChild); + std::shared_ptr<CControlData> GetChild(CControl &inParent, long inIndex); + std::shared_ptr<CControlData> GetParent(CControl &inControl); + + SReverseIterator GetRChildren(CControl &inControl); + SIterator GetChildren(CControl &inControl); + }; +} +} + +#endif diff --git a/src/Authoring/Qt3DStudio/Controls/ControlGraphIterators.h b/src/Authoring/Qt3DStudio/Controls/ControlGraphIterators.h new file mode 100644 index 00000000..ae2ffcf6 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/ControlGraphIterators.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#pragma once +#ifndef CONTROLGRAPHITERATORSH +#define CONTROLGRAPHITERATORSH +#include <vector> + +namespace Q3DStudio { +namespace Control { + class CControlData; + using std::vector; + + namespace ControlGraph { + struct SDummyGraphNode; + + struct SIteratorBase + { + protected: + std::shared_ptr<CControlData> m_ControlData; + vector<SDummyGraphNode *> *m_Children; + long m_Index; + + public: + SIteratorBase() + : m_Children(nullptr) + , m_Index(0) + { + } + + SIteratorBase(std::shared_ptr<CControlData> data, + vector<SDummyGraphNode *> &inChildren) + : m_ControlData(data) + , m_Children(&inChildren) + , m_Index(0) + { + } + + SIteratorBase(const SIteratorBase &inOther) + : m_ControlData(inOther.m_ControlData) + , m_Children(inOther.m_Children) + , m_Index(inOther.m_Index) + { + } + + SIteratorBase &operator=(const SIteratorBase &inOther) + { + if (this != &inOther) { + m_ControlData = inOther.m_ControlData; + m_Children = inOther.m_Children; + m_Index = inOther.m_Index; + } + return *this; + } + + bool operator==(const SIteratorBase &other) { return m_Index == other.m_Index; } + bool operator!=(const SIteratorBase &other) { return m_Index != other.m_Index; } + + std::shared_ptr<CControlData> GetCurrent(); + + std::shared_ptr<CControlData> operator->() { return GetCurrent(); } + std::shared_ptr<CControlData> operator*() { return GetCurrent(); } + }; + + struct SReverseIterator : SIteratorBase + { + SReverseIterator() {} + SReverseIterator(std::shared_ptr<CControlData> data, + vector<SDummyGraphNode *> &inChildren) + : SIteratorBase(data, inChildren) + { + m_Index = (long)inChildren.size() - 1; + } + SReverseIterator(const SReverseIterator &inOther) + : SIteratorBase(inOther) + { + } + SReverseIterator &operator=(const SReverseIterator &inOther) + { + SIteratorBase::operator=(inOther); + return *this; + } + + bool IsDone() { return m_Children == nullptr || m_Index < 0; } + bool HasNext() { return m_Children && m_Index > -1; } + SReverseIterator &operator++() + { + --m_Index; + return *this; + } + SReverseIterator &operator+=(long inAmount) + { + m_Index -= inAmount; + return *this; + } + }; + + struct SIterator : SIteratorBase + { + SIterator() {} + SIterator(std::shared_ptr<CControlData> data, vector<SDummyGraphNode *> &inChildren) + : SIteratorBase(data, inChildren) + { + } + SIterator(const SReverseIterator &inOther) + : SIteratorBase(inOther) + { + } + SIterator &operator=(const SIterator &inOther) + { + SIteratorBase::operator=(inOther); + return *this; + } + + bool IsDone() { return m_Children == nullptr || m_Index >= (long)m_Children->size(); } + bool HasNext() { return m_Children && m_Index < (long)m_Children->size(); } + SIterator operator++() + { + ++m_Index; + return *this; + } + SIterator &operator+=(long inAmount) + { + m_Index += inAmount; + return *this; + } + }; + } +} +} +#endif
\ No newline at end of file diff --git a/src/Authoring/Qt3DStudio/Controls/OffscreenRenderer.cpp b/src/Authoring/Qt3DStudio/Controls/OffscreenRenderer.cpp new file mode 100644 index 00000000..e30797f7 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/OffscreenRenderer.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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 "OffscreenRenderer.h" +#include "AppFonts.h" + +/** + * Constructor: Overrides a protected constructor on the base class. Sets up + * all the requirements so that this renderer is valid. + */ +COffscreenRenderer::COffscreenRenderer(const QRect &inClippingRect) + : CWinRenderer() +{ + m_pixmap = QPixmap(inClippingRect.size()); + m_painter = new QPainter(&m_pixmap); + QFont font = CAppFonts::GetInstance()->GetNormalFont(); + m_painter->setFont(font); + + // Set up the specified clipping region. Renderer is now valid for use. + PushClippingRect(inClippingRect); +} + +//============================================================================= +/** + * Destructor + */ +COffscreenRenderer::~COffscreenRenderer() +{ + delete m_painter; + m_painter = nullptr; +} diff --git a/src/Authoring/Qt3DStudio/Controls/OffscreenRenderer.h b/src/Authoring/Qt3DStudio/Controls/OffscreenRenderer.h new file mode 100644 index 00000000..dccd259f --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/OffscreenRenderer.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +//============================================================================== +// Prefix +//============================================================================== +#ifndef INCLUDED_OFFSCREEN_RENDERER_H +#define INCLUDED_OFFSCREEN_RENDERER_H 1 + +//============================================================================== +// Includes +//============================================================================== +#include "WinRenderer.h" + +//============================================================================= +/** + * Class for creating a renderer compatible with the current display, but that + * does not actually draw to the display. Provided so that you controls can + * query text size outside of their draw functions. This class could be + * further extended to provide offscreen drawing and blitting, though it's + * not currently set up to do so. + */ +class COffscreenRenderer : public CWinRenderer +{ +public: + COffscreenRenderer(const QRect &inClippingRect); + virtual ~COffscreenRenderer(); + + QPixmap pixmap() const override { return m_pixmap;} + +protected: + QPixmap m_pixmap; +}; + +#endif // INCLUDED_OFFSCREEN_RENDERER_H diff --git a/src/Authoring/Qt3DStudio/Controls/Renderer.cpp b/src/Authoring/Qt3DStudio/Controls/Renderer.cpp new file mode 100644 index 00000000..5b0a6296 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/Renderer.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "Renderer.h" + +#include <QtCore/qpoint.h> +#include <QtCore/qrect.h> + +/** + * Draws the outline of a rectangle in the specified colors. + * + * @param inRect Rectangle to be outlined + * @param inTop Color of the top line + * @param inRight Color of the line on the right side + * @param inBottom Color of the line on the bottom + * @param inLeft Color of the line on the left side + */ +void CRenderer::DrawRectOutline(const QRect &inRect, const QColor &inTop, const QColor &inRight, + const QColor &inBottom, const QColor &inLeft) +{ + QPoint theUpperLeft(inRect.topLeft()); + QPoint theUpperRight(inRect.topRight()); + QPoint theLowerRight(inRect.bottomRight()); + QPoint theLowerLeft(inRect.bottomLeft()); + + // Top + PushPen(inTop, 1); + MoveTo(theUpperLeft.x(), theUpperLeft.y()); + LineTo(theUpperRight.x(), theUpperRight.y()); + PopPen(); + + // Right + PushPen(inRight, 1); + LineTo(theLowerRight.x(), theLowerRight.y()); + PopPen(); + + // Bottom + PushPen(inBottom, 1); + LineTo(theLowerLeft.x(), theLowerLeft.y()); + PopPen(); + + // Left + PushPen(inLeft, 1); + LineTo(theUpperLeft.x(), theUpperLeft.y()); + PopPen(); +} diff --git a/src/Authoring/Qt3DStudio/Controls/Renderer.h b/src/Authoring/Qt3DStudio/Controls/Renderer.h new file mode 100644 index 00000000..7ef77fe3 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/Renderer.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#ifndef INCLUDED_RENDERER_H +#define INCLUDED_RENDERER_H 1 + +#pragma once + +#include <QColor> +#include <QPoint> + +#include "CColor.h" + +QT_BEGIN_NAMESPACE +class QPainter; +class QPixmap; +class QRect; +class QSize; +class QString; +QT_END_NAMESPACE + +class CRenderer +{ + typedef std::vector<QPoint> TTranslationList; + +public: + virtual ~CRenderer() {} + + virtual QPainter* GetPainter() = 0; + virtual void FillSolidRect(const QRect &inCoordinates, const QColor &inColor) = 0; + virtual void FillRoundedRect(const QRect &inCoordinates, const QColor &inColor, + bool vertical) = 0; + virtual void MoveTo(const QPoint &inPoint) = 0; + virtual void MoveTo(long inX, long inY) = 0; + virtual void LineTo(const QPoint &inPoint) = 0; + virtual void LineTo(long inX, long inY) = 0; + + virtual void PushPen(const QColor &inColor, + int inWidth = 1) = 0; //, UINT inStyle = PS_SOLID ); + virtual void PopPen() = 0; + virtual void BitBltFrom(const QRect &inRect, CRenderer *inRenderer, long inXSrc, long inYSrc) = 0; + + virtual void DrawText(float inX, float inY, const QString &inText) = 0; + virtual void DrawText(float inX, float inY, const QString &inText, + const QRect &inBoundingRect, const QColor &inColor = Qt::black) = 0; + virtual void DrawBoldText(float inX, float inY, const QString &inText, + const QRect &inBoundingRect, const QColor &inColor = Qt::black) = 0; + + virtual QSize GetTextSize(const QString &inText) = 0; + + virtual void DrawBitmap(const QPoint &inPos, const QPixmap &inImage) = 0; + virtual void Draw3dRect(const QRect &inRect, const QColor &inTopLeftColor, + const QColor &inBottomRightColor) = 0; + virtual void DrawRectOutline(const QRect &inRect, const QColor &inTop, const QColor &inRight, + const QColor &inBottom, const QColor &inLeft); + virtual void DrawGradientBitmap(const QRect &inRct, const QColor &inBeginColor, bool inInverted, + double inScalingFactor = .99) = 0; + + virtual void DrawGradient(const QRect &inRect, const QColor &inBeginColor, + const QColor &inEndColor) = 0; + + virtual void PushTranslation(const QPoint &inTranslation) = 0; + virtual void PopTranslation() = 0; + virtual QPoint GetTranslation() = 0; + + virtual QRect GetClippingRect() = 0; + virtual void PushClippingRect(const QRect &inRect) = 0; + virtual void PushAbsoluteClippingRect(const QRect &inRect) = 0; + virtual void PopClippingRect() = 0; + + virtual void FillHashed(const QRect &inRect, const QColor &inForeGroundColor) = 0; + + virtual QPixmap pixmap() const = 0; + +protected: + QPoint m_Translation; + TTranslationList m_Translations; +}; +#endif // INCLUDED_RENDERER_H diff --git a/src/Authoring/Qt3DStudio/Controls/WidgetControl.cpp b/src/Authoring/Qt3DStudio/Controls/WidgetControl.cpp new file mode 100644 index 00000000..207abed0 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/WidgetControl.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** 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 "WidgetControl.h" + +#include "Control.h" +#include "DropSource.h" +#include "IDragable.h" +#include "OffscreenRenderer.h" +#include "Pt.h" +#include "Rct.h" +#include "Qt3DSFile.h" + +#include <QtGui/qdrag.h> +#include <QtGui/qevent.h> +#include <QtGui/qpainter.h> +#include <QtWidgets/qmenu.h> + +WidgetControl::WidgetControl(CControl *control, QWidget *parent) + : QWidget(parent) + , m_control(control) +{ + Q_ASSERT(control); + setControlSize(sizeHint()); +} + +WidgetControl::~WidgetControl() +{ +} + +bool WidgetControl::event(QEvent *event) +{ + if (event->type() == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast<QKeyEvent *>(event); + if (ke->key() == Qt::Key_C && ke->modifiers() == Qt::ControlModifier) + m_control->OnKeyDown(ke->key(), ke->modifiers()); + } + return QWidget::event(event); +} + +void WidgetControl::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); +} + +void WidgetControl::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + const auto boundRect = QRect(QPoint(0,0), size()); + CWinRenderer renderer(&painter, boundRect); + CRct rect(event->rect()); + m_control->OnDraw(&renderer, rect, true); + + QWidget::paintEvent(event); +} + +void WidgetControl::resizeEvent(QResizeEvent *event) +{ + setControlSize(event->size()); + QWidget::resizeEvent(event); +} + +void WidgetControl::keyPressEvent(QKeyEvent *event) +{ + QWidget::keyPressEvent(event); + m_control->OnKeyDown(event->key(), event->modifiers()); + m_control->OnChar(event->text(), event->modifiers()); +} + +void WidgetControl::keyReleaseEvent(QKeyEvent *event) +{ + m_control->OnKeyUp(event->key(), event->modifiers()); + QWidget::keyReleaseEvent(event); +} + +void WidgetControl::mousePressEvent(QMouseEvent *event) +{ + const auto pos = CPt(event->pos()); + if (m_isLeftMouseDown) + m_control->OnMouseUp(pos, event->modifiers()); + + m_isLeftMouseDown = (event->button() == Qt::LeftButton); + if (m_isLeftMouseDown) + m_control->OnMouseDown(pos, event->modifiers()); + else + m_control->OnMouseRDown(pos, event->modifiers()); + + setFocus(); + QWidget::mousePressEvent(event); +} + +void WidgetControl::mouseReleaseEvent(QMouseEvent *event) +{ + const auto pos = CPt(event->pos()); + if (event->button() == Qt::LeftButton) { + m_isLeftMouseDown = false; + m_control->OnMouseUp(pos, event->modifiers()); + } else { + m_control->OnMouseRUp(pos, event->modifiers()); + } + + QWidget::mouseReleaseEvent(event); +} + +void WidgetControl::mouseMoveEvent(QMouseEvent *event) +{ + m_control->OnMouseMove(event->pos(), event->modifiers()); + QWidget::mouseMoveEvent(event); +} + +void WidgetControl::mouseDoubleClickEvent(QMouseEvent *event) +{ + // call QWidget handler first to not deliver OnMouseDown after OnMouseDoubleClick + QWidget::mouseDoubleClickEvent(event); + m_control->OnMouseDoubleClick(event->pos(), event->modifiers()); +} + +void WidgetControl::wheelEvent(QWheelEvent *event) +{ + m_control->OnMouseWheel(event->pos(), event->angleDelta().y(), event->modifiers()); + QWidget::wheelEvent(event); +} + +void WidgetControl::enterEvent(QEvent *event) +{ + setMouseTracking(true); + m_control->OnMouseHover(mapFromGlobal(QCursor::pos()), {}); + QWidget::enterEvent(event); +} + +void WidgetControl::leaveEvent(QEvent *event) +{ + setMouseTracking(false); + m_control->OnMouseOut(mapFromGlobal(QCursor::pos()), {}); + QWidget::leaveEvent(event); +} + +void WidgetControl::focusInEvent(QFocusEvent *event) +{ + m_control->OnGainFocus(); + QWidget::focusInEvent(event); +} + +void WidgetControl::focusOutEvent(QFocusEvent *event) +{ + if (!m_isContextMenuShown) + m_control->OnLoseFocus(); + QWidget::focusOutEvent(event); +} + +QSize WidgetControl::sizeHint() const +{ + const auto preferredSize = m_control->GetPreferredSize(); + return QSize(preferredSize.x, preferredSize.y); +} + +/* + * CPaletteManager::GetTimelineControl() needs a way of accessing + * the CControl inside the widget + */ +CControl *WidgetControl::getControl() const +{ + return m_control; +} + +void WidgetControl::setControlSize(const QSize &size) +{ + m_control->SetSize(size.width(), size.height()); +} + +bool WidgetControl::OnDragWithin(CDropSource &inSource) +{ + bool theReturn = false; + CPt thePoint = inSource.GetCurrentPoint(); + Qt::KeyboardModifiers theFlags = inSource.GetCurrentFlags(); + CDropTarget *theDropTarget = m_control->FindDropCandidate( + thePoint, theFlags, static_cast<EStudioObjectType>(inSource.GetObjectType()), + static_cast<Q3DStudio::DocumentEditorFileType::Enum>(inSource.getFileType())); + + if (theDropTarget) { + theReturn = theDropTarget->Accept(inSource); + delete theDropTarget; + } + return theReturn; +} + +bool WidgetControl::OnDragReceive(CDropSource &inSource) +{ + bool theReturn = false; + CPt thePoint = inSource.GetCurrentPoint(); + Qt::KeyboardModifiers theFlags = inSource.GetCurrentFlags(); + + CDropTarget *theDropTarget = m_control->FindDropCandidate( + thePoint, theFlags, static_cast<EStudioObjectType>(inSource.GetObjectType()), + static_cast<Q3DStudio::DocumentEditorFileType::Enum>(inSource.getFileType())); + + if (theDropTarget) { + theReturn = theDropTarget->Drop(inSource); + delete theDropTarget; + } + return theReturn; +} + +void WidgetControl::OnDragLeave() +{ + m_control->OnMouseMove(CPt(-1, -1), 0); +} + +void WidgetControl::OnReflectMouse(CPt &inPoint, Qt::KeyboardModifiers inFlags) +{ + // Notify the control that the mouse moved + m_control->OnMouseMove(inPoint, inFlags /*CHotKeys::GetCurrentKeyModifiers( )*/); + + // If the control invalidated because of a mouse event then we want to do an immediate redraw. + // this ensures consistent visible feedback. + if (m_control->IsChildInvalidated()) + repaint(); +} diff --git a/src/Authoring/Qt3DStudio/Controls/WidgetControl.h b/src/Authoring/Qt3DStudio/Controls/WidgetControl.h new file mode 100644 index 00000000..277a2e17 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/WidgetControl.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef WIDGETCONTROL_H +#define WIDGETCONTROL_H + +#include "Control.h" +#include "DropContainer.h" + +#include <QtWidgets/qwidget.h> + +class CRenderer; +class WidgetControl; + +class WidgetControl : public QWidget, public CWinDropContainer +{ + Q_OBJECT +public: + explicit WidgetControl(CControl *control, QWidget *parent = nullptr); + virtual ~WidgetControl(); + void setContextMenuShown(bool shown) { m_isContextMenuShown = shown; } + +protected: + bool event(QEvent *event) override; + void showEvent(QShowEvent *event) override; + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + void wheelEvent(QWheelEvent *event) override; + void enterEvent(QEvent *event) override; + void leaveEvent(QEvent *event) override; + void focusInEvent(QFocusEvent *event) override; + void focusOutEvent(QFocusEvent *event) override; + +public: + QSize sizeHint() const override; + CControl *getControl() const; + +protected: + bool OnDragWithin(CDropSource &inSource) override; + bool OnDragReceive(CDropSource &inSource) override; + void OnDragLeave() override; + void OnReflectMouse(CPt &inPoint, Qt::KeyboardModifiers inFlags) override; + +private: + void setControlSize(const QSize &size); + + CControl *m_control; + bool m_isLeftMouseDown = false; + bool m_isContextMenuShown = false; +}; + +#endif // WIDGETCONTROL_H diff --git a/src/Authoring/Qt3DStudio/Controls/WinRenderer.cpp b/src/Authoring/Qt3DStudio/Controls/WinRenderer.cpp new file mode 100644 index 00000000..1fd98734 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/WinRenderer.cpp @@ -0,0 +1,518 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "WinRenderer.h" +#include "MasterP.h" +#include "CoreUtils.h" + +//============================================================================= +/** + * Leaves the Renderer in an invalid state, only for subclasses. + * In order for this renderer to be valid, you must do two things: (1) you must + * create a CDC and set m_DC equal to it, and (2) you must call PushClippingRect. + */ +CWinRenderer::CWinRenderer() + : m_painter(nullptr) +{ +} + +//============================================================================= +/** + * Leaves the Renderer in an invalid state, only for subclasses. + * PushClippingRect must be called in order for this to become valid. + */ +CWinRenderer::CWinRenderer(QPainter *inDC) +{ + m_painter = inDC; +} + +CWinRenderer::CWinRenderer(QPainter *inDC, const QRect &inClippingRect) +{ + m_painter = inDC; + + PushClippingRect(inClippingRect); +} + +CWinRenderer::~CWinRenderer() +{ + m_Pens.clear(); +} + +//============================================================================= +/** + * Draws a rectangle and fills it with inColor. + * The rectangle has no border and is solid. + * The coordinates are converted to global space using the current translation. + * @param inCoordinates the coordinates of the rectangle. + * @param inColor the color of the rectangle to draw. + */ +void CWinRenderer::FillSolidRect(const QRect &inCoordinates, const QColor &inColor) +{ + QRect theRect(inCoordinates.topLeft() + m_Translation, + inCoordinates.size()); + + m_painter->fillRect(theRect, inColor); +} + +//============================================================================= +/** + * Draws a rounded rectangle (which actually is just a line) and fills it with inColor. + * The coordinates are converted to global space using the current translation. + * @param inCoordinates the coordinates of the rectangle. + * @param inColor the color of the rectangle to draw. + */ +void CWinRenderer::FillRoundedRect(const QRect &inCoordinates, const QColor &inColor, + bool vertical) +{ + QPen previousPen = m_painter->pen(); + QRect theRect(inCoordinates.topLeft() + m_Translation, inCoordinates.size()); + QPointF startPoint = (theRect.bottomLeft() + theRect.topLeft()) / 2.0; + QPointF endPoint = (theRect.bottomRight() + theRect.topRight()) / 2.0; + qreal lineWidth = theRect.bottom() - theRect.top() + 1.0; + + if (vertical) { + lineWidth = theRect.right() - theRect.left() + 1.0; + startPoint = (theRect.topRight() + theRect.topLeft()) / 2.0; + endPoint = (theRect.bottomRight() + theRect.bottomLeft()) / 2.0; + } + + m_painter->setPen(QPen(inColor, lineWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + m_painter->drawLine(startPoint, endPoint); + + // Restore previous pen + m_painter->setPen(previousPen); +} + +//============================================================================= +/** + * Move the pen to the position. + * This will put the pen at inPoint but will not draw a line there from the + * current location. + * @param inPoint the location to move to, in local coordinates. + */ +void CWinRenderer::MoveTo(const QPoint &inPoint) +{ + m_currentPos = inPoint + m_Translation; +} + +//============================================================================= +/** + * Move the pen to the position. + * This will put the pen to the point but will not draw a line there from the + * current location. + * @param inX the X location to move to, in local coordinates. + * @param inY the Y location to move to, in local coordinates. + */ +void CWinRenderer::MoveTo(long inX, long inY) +{ + MoveTo(QPoint(inX, inY)); +} + +//============================================================================= +/** + * Draw a line from the current pen location to inPoint. + * This will draw a line to inPoint and move the current location of the pen + * to inPoint. + * @param inPoint the location to draw to, in local coordinates. + */ +void CWinRenderer::LineTo(const QPoint &inPoint) +{ + const QPoint point = inPoint + m_Translation; + m_painter->drawLine(m_currentPos, point); + m_painter->drawLine(point, QPoint(point.x(), point.y() + 1)); + m_currentPos = point; +} + +//============================================================================= +/** + * Draw a line from the current pen location to the point. + * This will draw a line to the point and move the current location of the pen + * to inPoint. + * @param inX the X coordinate of the point to draw to, in local coordinates. + * @param inY the Y coordinate of the point to draw to, in local coordinates. + */ +void CWinRenderer::LineTo(long inX, long inY) +{ + LineTo(QPoint(inX, inY)); +} + +//============================================================================= +/** + * Change the active pen color. + * This acts as a stack so that it does not interfere with previous components. + * Once the color is done being used PopPen should be called. + * @param inColor the color to make the current pen. + * @param inWidth the pen width, in pixels. + */ +void CWinRenderer::PushPen(const QColor &inColor, int inWidth) +{ + QPen thePen = GetPen(inColor, inWidth, Qt::SolidLine); + + QPen theCurrentPen = m_painter->pen(); + m_painter->setPen(thePen); + m_PenList.push_back(theCurrentPen); +} + +void CWinRenderer::PopPen() +{ + QPen thePen = m_PenList.back(); + m_PenList.pop_back(); + m_painter->setPen(thePen); +} + +//============================================================================= +/** + * Copy the bits from another renderer into this one. + * This will copy the rect from inRenderer into this renderer. + * inRect will be offset by this renderer's current translation. inXSrc and inYSrc + * will be offset by inRenderer's current translation. + * @param inRect the position and size of the area to be copied into this renderer. + * @param inPixmap the renderer to copy from. + * @param xSrc the x location of the location to copy from in inRenderer. + * @param ySrc the y location of the location to copy from in inRenderer. + */ +void CWinRenderer::BitBltFrom(const QRect &inRect, CRenderer *inRenderer, long inXSrc, long inYSrc) +{ + const auto inTranslation = inRenderer->GetTranslation(); + inXSrc += inTranslation.x(); + inYSrc += inTranslation.y(); + + auto srcRect = inRect; + auto destRect = inRect; + destRect.translate(m_Translation); + srcRect.moveTo(inXSrc, inYSrc); + m_painter->save(); + m_painter->setCompositionMode(QPainter::CompositionMode_DestinationOver); + m_painter->drawPixmap(destRect, inRenderer->pixmap(), srcRect); + m_painter->restore(); +} + +//============================================================================= +/** + * Draws text out without clipping it. + * @param inPoint the point at which to draw the text. (upper left corner) + * @param inText the text to draw. + */ +void CWinRenderer::DrawText(float inX, float inY, const QString &inText) +{ + inX += m_Translation.x(); + inY += m_Translation.y(); + m_painter->drawText(QPointF(inX, inY), inText); +} + +//============================================================================= +/** + * Draws text out to a clipped rectangle. + * If any text occurs outside the bounding box then it will be clipped. + * @param inPoint the point at which to draw the text. (upper left corner) + * @param inText the text to draw. + * @param inBoundingBox the bounding box used to clip the text. + * @param inColor color to draw the text in + */ +void CWinRenderer::DrawText(float inX, float inY, const QString &inText, + const QRect &inBoundingBox, const QColor &inColor) +{ + inX += m_Translation.x(); + inY += m_Translation.y(); + + QRectF rect(inBoundingBox); + rect.translate(inX, inY); + m_painter->save(); + QPen pen(inColor); + m_painter->setPen(pen); + m_painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, inText); + m_painter->restore(); +} + +//============================================================================= +/** + * Draws BOLD text out to a clipped rectangle. + * If any text occurs outside the bounding box then it will be clipped. + * @param inPoint the point at which to draw the text. (upper left corner) + * @param inText the text to draw. + * @param inBoundingBox the bounding box used to clip the text. + * @param inColor color to draw the text in + */ +void CWinRenderer::DrawBoldText(float inX, float inY, const QString &inText, + const QRect &inBoundingBox, const QColor &inColor) +{ + inX += m_Translation.x(); + inY += m_Translation.y(); + + QRectF rect(inBoundingBox); + rect.translate(inX, inY); + m_painter->save(); + QPen pen(inColor); + m_painter->setPen(pen); + QFont font = m_painter->font(); + font.setBold(true); + m_painter->setFont(font); + m_painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, inText); + m_painter->restore(); +} + +//============================================================================= +/** + * Gets the dimensions of the text string as it would be written out to the + * screen. + * @param inText the text to check the length on. + * @return the length of the text in pixels. + */ +QSize CWinRenderer::GetTextSize(const QString &inText) +{ + QFontMetrics fm = m_painter->fontMetrics(); + return fm.size(Qt::TextSingleLine, inText); +} + +//============================================================================= +/** + * Draws a a three-dimensional rectangle with the top and left sides in the + * color specified by inTopLeftColor and the bottom and right sides in the color + * specified by inBottomRightColor. + * + * @param inRect The rectangle to draw + * @param inTopLeftColor Color for the top and left sides of the rect + * @param inBottomRightColor Color for the bottom and right sides of the rect + */ +void CWinRenderer::Draw3dRect(const QRect &inRect, const QColor &inTopLeftColor, + const QColor &inBottomRightColor) +{ + auto rect = inRect; + rect.translate(m_Translation); + m_painter->drawRect(rect); + m_painter->save(); + m_painter->setPen(inTopLeftColor); + m_painter->drawLine(rect.topLeft(), rect.bottomLeft()); + m_painter->drawLine(rect.topLeft(), rect.topRight()); + m_painter->setPen(inBottomRightColor); + m_painter->drawLine(rect.bottomLeft(), rect.bottomRight()); + m_painter->drawLine(rect.bottomRight(), rect.topRight()); + m_painter->restore(); +} + +//============================================================================== +/** + * DrawBitmap + * + * Draw a bitmap given a device context, position and HBITMAP handle. + * + * @param inDC Device context for drawing + * @param inPos CPoint position for drawing + * @param inBitmap Handle of the bitmap to draw + */ +//============================================================================== +void CWinRenderer::DrawBitmap(const QPoint &inPos, const QPixmap &inImage) +{ + m_painter->save(); + m_painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + m_painter->drawPixmap(inPos + m_Translation, inImage); + m_painter->restore();; +} + +void CWinRenderer::PushTranslation(const QPoint &inTranslation) +{ + m_Translation += inTranslation; + + m_Translations.push_back(inTranslation); +} + +void CWinRenderer::PopTranslation() +{ + QPoint thePreviousTranslation = m_Translations.back(); + m_Translation -= thePreviousTranslation; + + m_Translations.pop_back(); +} + +QPoint CWinRenderer::GetTranslation() +{ + return m_Translation; +} + +QPainter* CWinRenderer::GetPainter() +{ + return m_painter; +} + +//============================================================================== +/** + * Get the current clipping rect. + * The clipping rect is the boundary of pixels that will be drawn to the DC. + * This can be used to not draw non-visible objects. + * @return the clipping rect in local coordinates. + */ +QRect CWinRenderer::GetClippingRect() +{ + QRect theClippingRect = m_painter->clipBoundingRect().toRect(); + theClippingRect.translate(-m_Translation); + + return theClippingRect; +} + +//============================================================================== +/** + * Push a clipping rect onto the stack of clipping rects. + * This will cause any drawing outside of the clipping rect to be ignored. The + * Control class also uses this to not draw objects outside of this rect. + * @param inClippingRect the new clipping rect, in local coordinates. + */ +void CWinRenderer::PushClippingRect(const QRect &inClippingRect) +{ + QRect clippingRect(inClippingRect); + clippingRect.translate(m_Translation); + + const QRegion currentRegion = m_painter->clipRegion(); + m_painter->setClipRect(clippingRect); + + m_ClippingRegions.push_back(currentRegion); +} + +//============================================================================== +/** + * Pop the current clipping rect. + * This will change the clipping rect to use the one previous to the current + * one. + */ +void CWinRenderer::PopClippingRect() +{ + if (m_ClippingRegions.size() > 1) { + QRegion thePreviousRegion = m_ClippingRegions.back(); + m_painter->setClipRegion(thePreviousRegion); + m_ClippingRegions.pop_back(); + } +} + +//============================================================================== +/** + * DrawGradiantBitmap + * + * Draws a gradiant based on the begin color + * + * @param inRct Rct tof the control + * @param inBeginColor color to start with + * @param inInverted true if this is inverted + */ +void CWinRenderer::DrawGradientBitmap(const QRect &inRct, const QColor &inBeginColor, bool inInverted, + double inScalingFactor) +{ + QRect rect(inRct); + QRect theClippingRect = GetClippingRect(); + rect &= theClippingRect; + rect.translate(m_Translation); + + long theHeight = rect.height(); + long theWidth = rect.width(); + + QImage theCompatibleBitmap(1, theHeight, QImage::Format_RGB32); + + int theR = inBeginColor.red(); + int theG = inBeginColor.green(); + int theB = inBeginColor.blue(); + + double theExtraRModifier = inScalingFactor * (1.0 - (theR / 255.0)); + double theExtraGModifier = inScalingFactor * (1.0 - (theG / 255.0)); + double theExtraBModifier = inScalingFactor * (1.0 - (theB / 255.0)); + + for (long thePixel = 0; thePixel < theHeight; ++thePixel) { + double theNormPixel = (double)thePixel / (theHeight * 4.8); + double theCos = 1.0 / (theNormPixel * theNormPixel + 1.0); + double theRValue = (double)theR * (theCos + theExtraRModifier); + double theGValue = (double)theG * (theCos + theExtraGModifier); + double theBValue = (double)theB * (theCos + theExtraBModifier); + + QColor theTempColor(::dtoi(theRValue), ::dtoi(theGValue), ::dtoi(theBValue)); + if (inInverted) { + theCompatibleBitmap.setPixelColor(0, qMax(0l, theHeight - thePixel - 2), theTempColor); + } else { + theCompatibleBitmap.setPixelColor(0, thePixel, theTempColor); + } + theExtraRModifier *= 0.3; + theExtraGModifier *= 0.3; + theExtraBModifier *= 0.3; + } + + m_painter->save(); + m_painter->drawImage(QRect(rect.x(), rect.y(), theWidth, theHeight), theCompatibleBitmap); + m_painter->restore(); +} + +//============================================================================= +/** + * Draw a gradient over inRect. + * This will blend horizontally across the rect from inBeginColor into inEndColor. + * This does linear interpolation. + * @param inRect the rect to draw on. + * @param inBeginColor the start (left most) color. + * @param inEndColor the final (right most) color. + */ +void CWinRenderer::DrawGradient(const QRect &inRect, const QColor &inBeginColor, const QColor &inEndColor) +{ + const QRect rect = inRect.translated(m_Translation); + QLinearGradient gradient(rect.topLeft(), rect.topRight()); + gradient.setColorAt(0, inBeginColor); + gradient.setColorAt(1, inEndColor); + + QBrush brush(gradient); + m_painter->fillRect(rect, brush); +} + +void CWinRenderer::FillHashed(const QRect &inRect, const QColor &inForegroundColor) +{ + m_painter->save(); + + QBrush theBrush(inForegroundColor, Qt::BDiagPattern); + QPen pen(inForegroundColor); + m_painter->setPen(pen); + m_painter->setBrush(theBrush); + m_painter->drawRect(inRect.translated(m_Translation)); + + m_painter->restore(); +} + +QPen CWinRenderer::GetPen(const QColor &inColor, int inWidth, Qt::PenStyle inStyle) +{ + QPen thePen; + SPenInfo theInfo = { inColor, inWidth, inStyle }; + TPenMap::iterator thePos = m_Pens.find(theInfo); + if (thePos != m_Pens.end()) { + thePen = thePos->second; + } else { + thePen.setColor(inColor); + thePen.setWidth(inWidth); + m_Pens[theInfo] = thePen; + } + return thePen; +} + +QPixmap CWinRenderer::pixmap() const +{ + return {}; +} diff --git a/src/Authoring/Qt3DStudio/Controls/WinRenderer.h b/src/Authoring/Qt3DStudio/Controls/WinRenderer.h new file mode 100644 index 00000000..3c56145b --- /dev/null +++ b/src/Authoring/Qt3DStudio/Controls/WinRenderer.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#ifndef INCLUDED_WIN_RENDERER_H +#define INCLUDED_WIN_RENDERER_H 1 + +#pragma once + +#include <vector> +#include <map> + +#include "Pt.h" +#include "Rct.h" +#include "Renderer.h" + +#include <QPainter> +#include <QRegion> + +QT_BEGIN_NAMESPACE +class QPixmap; +QT_END_NAMESPACE + +class CWinRenderer : public CRenderer +{ + typedef std::vector<QPen> TPenList; + typedef std::vector<QRegion> TClippingRegions; + +protected: + CWinRenderer(); ///< Leaves CRenderer in an invalid state, only for subclasses + CWinRenderer(QPainter *inDC); ///< Leaves CRenderer in an invalid state, only for subclasses + +public: + CWinRenderer(QPainter *inDC, const QRect &inClippingRect); + virtual ~CWinRenderer(); + + void FillSolidRect(const QRect &inCoordinates, const QColor &inColor) override; + void FillRoundedRect(const QRect &inCoordinates, const QColor &inColor, + bool vertical) override; + + void MoveTo(const QPoint &inPoint) override; + void MoveTo(long inX, long inY) override; + void LineTo(const QPoint &inPoint) override; + void LineTo(long inX, long inY) override; + + void PushPen(const QColor &inColor, int inWidth = 1) override; + void PopPen() override; + void BitBltFrom(const QRect &inRect, CRenderer *inRenderer, long inXSrc, long inYSrc) override; + + void DrawText(float inX, float inY, const QString &inText) override; + void DrawText(float inX, float inY, const QString &inText, + const QRect &inBoundingRect, const QColor &inColor = Qt::black) override; + void DrawBoldText(float inX, float inY, const QString &inText, + const QRect &inBoundingRect, const QColor &inColor = Qt::black) override; + + QSize GetTextSize(const QString &inText) override; + + void DrawBitmap(const QPoint &inPos, const QPixmap &inImage) override; + void Draw3dRect(const QRect &inRect, const QColor &inTopLeftColor, + const QColor &inBottomRightColor) override; + void DrawGradientBitmap(const QRect &inRct, const QColor &inBeginColor, bool inInverted, + double inScalingFactor = .99) override; + + void DrawGradient(const QRect &inRect, const QColor &inBeginColor, const QColor &inEndColor) override; + + void PushTranslation(const QPoint &inTranslation) override; + void PopTranslation() override; + QPoint GetTranslation() override; + + QRect GetClippingRect() override; + void PushClippingRect(const QRect &inRect) override; + void PopClippingRect() override; + void PushAbsoluteClippingRect(const QRect &) override {} + void FillHashed(const QRect &inRect, const QColor &inForeGroundColor) override; + QPainter *GetPainter() override; + QPen GetPen(const QColor &inColor, int inWidth, Qt::PenStyle inStyle); + + QPixmap pixmap() const override; + +protected: + TPenList m_PenList; + TClippingRegions m_ClippingRegions; + QPainter *m_painter; + QPoint m_currentPos; + +protected: + struct SPenInfo + { + QColor Color; + long Width; + Qt::PenStyle Style; + }; + + class CPenInfoLessThan : public std::binary_function<const SPenInfo &, const SPenInfo &, bool> + { + public: + inline bool operator()(const SPenInfo &inValL, const SPenInfo &inValR) const + { + return memcmp(&inValL, &inValR, sizeof(SPenInfo)) < 0; + } + }; + + typedef std::map<SPenInfo, QPen, CPenInfoLessThan> TPenMap; + TPenMap m_Pens; +}; +#endif // INCLUDED_WIN_RENDERER_H |