diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp index 5af44a81bc..4b8fbca36a 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp @@ -1,68 +1,68 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -namespace AzFramework -{ - ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta) - { - if (clickEvent == ClickEvent::Down) - { - const auto now = std::chrono::steady_clock::now(); - if (m_tryBeginTime) - { - const std::chrono::duration diff = now - m_tryBeginTime.value(); - if (diff.count() < m_doubleClickInterval) - { - return ClickOutcome::Nil; - } - } - - m_detectionState = DetectionState::WaitingForMove; - m_moveAccumulator = 0.0f; - - m_tryBeginTime = now; - } - else if (clickEvent == ClickEvent::Up) - { - const auto clickOutcome = [detectionState = m_detectionState] { - if (detectionState == DetectionState::WaitingForMove) - { - return ClickOutcome::Click; - } - if (detectionState == DetectionState::Moved) - { - return ClickOutcome::Release; - } - return ClickOutcome::Nil; - }(); - - m_detectionState = DetectionState::Nil; - return clickOutcome; - } - - if (m_detectionState == DetectionState::WaitingForMove) - { - // only allow the action to begin if the mouse has been moved a small amount - m_moveAccumulator += ScreenVectorLength(cursorDelta); - if (m_moveAccumulator > m_deadZone) - { - m_detectionState = DetectionState::Moved; - return ClickOutcome::Move; - } - } - - return ClickOutcome::Nil; - } -} // namespace AzFramework +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace AzFramework +{ + ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta) + { + if (clickEvent == ClickEvent::Down) + { + const auto now = std::chrono::steady_clock::now(); + if (m_tryBeginTime) + { + const std::chrono::duration diff = now - m_tryBeginTime.value(); + if (diff.count() < m_doubleClickInterval) + { + return ClickOutcome::Nil; + } + } + + m_detectionState = DetectionState::WaitingForMove; + m_moveAccumulator = 0.0f; + + m_tryBeginTime = now; + } + else if (clickEvent == ClickEvent::Up) + { + const auto clickOutcome = [detectionState = m_detectionState] { + if (detectionState == DetectionState::WaitingForMove) + { + return ClickOutcome::Click; + } + if (detectionState == DetectionState::Moved) + { + return ClickOutcome::Release; + } + return ClickOutcome::Nil; + }(); + + m_detectionState = DetectionState::Nil; + return clickOutcome; + } + + if (m_detectionState == DetectionState::WaitingForMove) + { + // only allow the action to begin if the mouse has been moved a small amount + m_moveAccumulator += ScreenVectorLength(cursorDelta); + if (m_moveAccumulator > m_deadZone) + { + m_detectionState = DetectionState::Moved; + return ClickOutcome::Move; + } + } + + return ClickOutcome::Nil; + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CursorState.h b/Code/Framework/AzFramework/AzFramework/Viewport/CursorState.h index 8bcbaabdee..437392f0fc 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CursorState.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CursorState.h @@ -1,56 +1,56 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#pragma once - -#include - -#include - -namespace AzFramework -{ - //! Utility type to wrap a current and last cursor position. - struct CursorState - { - //! Returns the delta between the current and last cursor position. - [[nodiscard]] ScreenVector CursorDelta() const; - //! Call this in a 'handle event' call to update the most recent cursor position. - void SetCurrentPosition(const ScreenPoint& currentPosition); - //! Call this in an 'update' call to copy the current cursor position to the last - //! cursor position. - void Update(); - - private: - AZStd::optional m_lastCursorPosition; - AZStd::optional m_currentCursorPosition; - }; - - inline void CursorState::SetCurrentPosition(const ScreenPoint& currentPosition) - { - m_currentCursorPosition = currentPosition; - } - - inline ScreenVector CursorState::CursorDelta() const - { - return m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value() - ? m_currentCursorPosition.value() - m_lastCursorPosition.value() - : ScreenVector(0, 0); - } - - inline void CursorState::Update() - { - if (m_currentCursorPosition.has_value()) - { - m_lastCursorPosition = m_currentCursorPosition; - } - } -} // namespace AzFramework +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include + +namespace AzFramework +{ + //! Utility type to wrap a current and last cursor position. + struct CursorState + { + //! Returns the delta between the current and last cursor position. + [[nodiscard]] ScreenVector CursorDelta() const; + //! Call this in a 'handle event' call to update the most recent cursor position. + void SetCurrentPosition(const ScreenPoint& currentPosition); + //! Call this in an 'update' call to copy the current cursor position to the last + //! cursor position. + void Update(); + + private: + AZStd::optional m_lastCursorPosition; + AZStd::optional m_currentCursorPosition; + }; + + inline void CursorState::SetCurrentPosition(const ScreenPoint& currentPosition) + { + m_currentCursorPosition = currentPosition; + } + + inline ScreenVector CursorState::CursorDelta() const + { + return m_currentCursorPosition.has_value() && m_lastCursorPosition.has_value() + ? m_currentCursorPosition.value() - m_lastCursorPosition.value() + : ScreenVector(0, 0); + } + + inline void CursorState::Update() + { + if (m_currentCursorPosition.has_value()) + { + m_lastCursorPosition = m_currentCursorPosition; + } + } +} // namespace AzFramework diff --git a/Code/Framework/Tests/ClickDetectorTests.cpp b/Code/Framework/Tests/ClickDetectorTests.cpp index 62f8069073..7e6f9634c8 100644 --- a/Code/Framework/Tests/ClickDetectorTests.cpp +++ b/Code/Framework/Tests/ClickDetectorTests.cpp @@ -1,142 +1,142 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include -#include - -namespace AzFramework -{ - std::ostream& operator<<(std::ostream& os, const ClickDetector::ClickOutcome clickOutcome) - { - switch (clickOutcome) - { - case ClickDetector::ClickOutcome::Click: - os << "ClickOutcome::Click"; - break; - case ClickDetector::ClickOutcome::Move: - os << "ClickOutcome::Move"; - break; - case ClickDetector::ClickOutcome::Release: - os << "ClickOutcome::Release"; - break; - case ClickDetector::ClickOutcome::Nil: - os << "ClickOutcome::Nil"; - break; - } - - return os; - } -} // namespace AzFramework - -namespace UnitTest -{ - using AzFramework::ClickDetector; - using AzFramework::ScreenVector; - - class ClickDetectorFixture : public ::testing::Test - { - public: - ClickDetector m_clickDetector; - }; - - TEST_F(ClickDetectorFixture, ClickIsDetectedWithNoMouseMovementOnMouseUp) - { - using ::testing::Eq; - - const ClickDetector::ClickOutcome initialDownOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); - const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); - - EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); - EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click)); - } - - TEST_F(ClickDetectorFixture, MoveIsDetectedWithMouseMovementAfterMouseDown) - { - using ::testing::Eq; - - const ClickDetector::ClickOutcome initialDownOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); - const ClickDetector::ClickOutcome initialMoveOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10)); - - EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); - EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move)); - } - - TEST_F(ClickDetectorFixture, ReleaseIsDetectedAfterMouseMovementOnMouseUp) - { - using ::testing::Eq; - - const ClickDetector::ClickOutcome initialDownOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); - // move - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10)); - const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); - - EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); - EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Release)); - } - - TEST_F(ClickDetectorFixture, MoveIsReturnedOnlyAfterFirstMouseMove) - { - using ::testing::Eq; - - const ClickDetector::ClickOutcome initialDownOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); - const ClickDetector::ClickOutcome initialMoveOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10)); - const ClickDetector::ClickOutcome secondaryMoveOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10)); - - EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); - EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move)); - EXPECT_THAT(secondaryMoveOutcome, Eq(ClickDetector::ClickOutcome::Nil)); - } - - TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterDoubleClick) - { - using ::testing::Eq; - - const ClickDetector::ClickOutcome initialDownOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); - const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); - const ClickDetector::ClickOutcome secondaryDownOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); - const ClickDetector::ClickOutcome secondaryUpOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); - - EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); - EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click)); - EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // double click - EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered - } - - TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterIgnoredDoubleClick) - { - using ::testing::Eq; - - const ClickDetector::ClickOutcome initialDownOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); - const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); - const ClickDetector::ClickOutcome secondaryDownOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(0, 0)); - const ClickDetector::ClickOutcome secondaryUpOutcome = - m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); - - EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); - EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click)); - EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // ignored double click - EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered - } -} // namespace UnitTest +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include +#include + +namespace AzFramework +{ + std::ostream& operator<<(std::ostream& os, const ClickDetector::ClickOutcome clickOutcome) + { + switch (clickOutcome) + { + case ClickDetector::ClickOutcome::Click: + os << "ClickOutcome::Click"; + break; + case ClickDetector::ClickOutcome::Move: + os << "ClickOutcome::Move"; + break; + case ClickDetector::ClickOutcome::Release: + os << "ClickOutcome::Release"; + break; + case ClickDetector::ClickOutcome::Nil: + os << "ClickOutcome::Nil"; + break; + } + + return os; + } +} // namespace AzFramework + +namespace UnitTest +{ + using AzFramework::ClickDetector; + using AzFramework::ScreenVector; + + class ClickDetectorFixture : public ::testing::Test + { + public: + ClickDetector m_clickDetector; + }; + + TEST_F(ClickDetectorFixture, ClickIsDetectedWithNoMouseMovementOnMouseUp) + { + using ::testing::Eq; + + const ClickDetector::ClickOutcome initialDownOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); + + EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); + EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click)); + } + + TEST_F(ClickDetectorFixture, MoveIsDetectedWithMouseMovementAfterMouseDown) + { + using ::testing::Eq; + + const ClickDetector::ClickOutcome initialDownOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome initialMoveOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10)); + + EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); + EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move)); + } + + TEST_F(ClickDetectorFixture, ReleaseIsDetectedAfterMouseMovementOnMouseUp) + { + using ::testing::Eq; + + const ClickDetector::ClickOutcome initialDownOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); + // move + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10)); + const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); + + EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); + EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Release)); + } + + TEST_F(ClickDetectorFixture, MoveIsReturnedOnlyAfterFirstMouseMove) + { + using ::testing::Eq; + + const ClickDetector::ClickOutcome initialDownOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome initialMoveOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10)); + const ClickDetector::ClickOutcome secondaryMoveOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(10, 10)); + + EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); + EXPECT_THAT(initialMoveOutcome, Eq(ClickDetector::ClickOutcome::Move)); + EXPECT_THAT(secondaryMoveOutcome, Eq(ClickDetector::ClickOutcome::Nil)); + } + + TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterDoubleClick) + { + using ::testing::Eq; + + const ClickDetector::ClickOutcome initialDownOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome secondaryDownOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome secondaryUpOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); + + EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); + EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click)); + EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // double click + EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered + } + + TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterIgnoredDoubleClick) + { + using ::testing::Eq; + + const ClickDetector::ClickOutcome initialDownOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome initialUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome secondaryDownOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Nil, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome secondaryUpOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0)); + + EXPECT_THAT(initialDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); + EXPECT_THAT(initialUpOutcome, Eq(ClickDetector::ClickOutcome::Click)); + EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // ignored double click + EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered + } +} // namespace UnitTest diff --git a/Code/Framework/Tests/CursorStateTests.cpp b/Code/Framework/Tests/CursorStateTests.cpp index 865db2391d..e04d04537b 100644 --- a/Code/Framework/Tests/CursorStateTests.cpp +++ b/Code/Framework/Tests/CursorStateTests.cpp @@ -1,54 +1,54 @@ -/* - * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or - * its licensors. - * - * For complete copyright and license terms please see the LICENSE at the root of this - * distribution (the "License"). All use of this software is governed by the License, - * or, if provided, by the license below or the license accompanying this file. Do not - * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -#include -#include - -namespace UnitTest -{ - using AzFramework::CursorState; - using AzFramework::ScreenVector; - using AzFramework::ScreenPoint; - - class CursorStateFixture : public ::testing::Test - { - public: - CursorState m_cursorState; - }; - - TEST_F(CursorStateFixture, CursorStateHasZeroDeltaInitially) - { - using ::testing::Eq; - EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0))); - } - - TEST_F(CursorStateFixture, CursorStateReturnsZeroDeltaAfterSingleMoveAndUpdate) - { - using ::testing::Eq; - - m_cursorState.SetCurrentPosition(ScreenPoint(10, 10)); - m_cursorState.Update(); - - EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0))); - } - - TEST_F(CursorStateFixture, CursorStateReturnsDeltaAfterSecondMoveAndUpdate) - { - using ::testing::Eq; - - m_cursorState.SetCurrentPosition(ScreenPoint(10, 10)); - m_cursorState.Update(); - m_cursorState.SetCurrentPosition(ScreenPoint(15, 22)); - - EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(5, 12))); - } -} // namespace UnitTest +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#include +#include + +namespace UnitTest +{ + using AzFramework::CursorState; + using AzFramework::ScreenVector; + using AzFramework::ScreenPoint; + + class CursorStateFixture : public ::testing::Test + { + public: + CursorState m_cursorState; + }; + + TEST_F(CursorStateFixture, CursorStateHasZeroDeltaInitially) + { + using ::testing::Eq; + EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0))); + } + + TEST_F(CursorStateFixture, CursorStateReturnsZeroDeltaAfterSingleMoveAndUpdate) + { + using ::testing::Eq; + + m_cursorState.SetCurrentPosition(ScreenPoint(10, 10)); + m_cursorState.Update(); + + EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(0, 0))); + } + + TEST_F(CursorStateFixture, CursorStateReturnsDeltaAfterSecondMoveAndUpdate) + { + using ::testing::Eq; + + m_cursorState.SetCurrentPosition(ScreenPoint(10, 10)); + m_cursorState.Update(); + m_cursorState.SetCurrentPosition(ScreenPoint(15, 22)); + + EXPECT_THAT(m_cursorState.CursorDelta(), Eq(ScreenVector(5, 12))); + } +} // namespace UnitTest diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index c62674122f..5737a188de 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -11,10 +11,13 @@ */ #include +#include #include #include #include -#include +#include + +//#define USE_TESTGEMDATA namespace O3DE::ProjectManager { @@ -24,60 +27,24 @@ namespace O3DE::ProjectManager m_gemModel = new GemModel(this); QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setMargin(0); setLayout(vLayout); QHBoxLayout* hLayout = new QHBoxLayout(); vLayout->addLayout(hLayout); - QWidget* filterPlaceholderWidget = new QWidget(); - filterPlaceholderWidget->setFixedWidth(250); - hLayout->addWidget(filterPlaceholderWidget); - m_gemListView = new GemListView(m_gemModel, this); - hLayout->addWidget(m_gemListView); - - QWidget* inspectorPlaceholderWidget = new QWidget(); - inspectorPlaceholderWidget->setFixedWidth(250); - hLayout->addWidget(inspectorPlaceholderWidget); + m_gemInspector = new GemInspector(m_gemModel, this); + m_gemInspector->setFixedWidth(320); // Start: Temporary gem test data +#ifdef USE_TESTGEMDATA + QVector testGemData = GenerateTestData(); + for (const GemInfo& gemInfo : testGemData) { - m_gemModel->AddGem(GemInfo("EMotion FX", - "O3DE Foundation", - "EMFX is a real-time character animation system. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", - (GemInfo::Android | GemInfo::iOS | GemInfo::macOS | GemInfo::Windows | GemInfo::Linux), - true)); - - m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("Atom", - "O3DE Foundation", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", - GemInfo::Android | GemInfo::Windows | GemInfo::Linux | GemInfo::macOS, - true)); - - m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("PhysX", - "O3DE London", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", - GemInfo::Android | GemInfo::Linux | GemInfo::macOS, - false)); - - m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("Certificate Manager", - "O3DE Irvine", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", - GemInfo::Windows, - false)); - - m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("Cloud Gem Framework", - "O3DE Seattle", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", - GemInfo::iOS | GemInfo::Linux, - false)); - - m_gemModel->AddGem(O3DE::ProjectManager::GemInfo("Achievements", - "O3DE Foundation", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - GemInfo::Android | GemInfo::Windows | GemInfo::Linux, - false)); + m_gemModel->AddGem(gemInfo); } +#else // End: Temporary gem test data auto result = PythonBindingsInterface::Get()->GetGems(); if (result.IsSuccess()) @@ -87,6 +54,105 @@ namespace O3DE::ProjectManager m_gemModel->AddGem(gemInfo); } } +#endif + + hLayout->addWidget(m_gemListView); + hLayout->addWidget(m_gemInspector); + + + // Select the first entry after everything got correctly sized + QTimer::singleShot(100, [=]{ + QModelIndex firstModelIndex = m_gemListView->model()->index(0,0); + m_gemListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect); + }); + } + + QVector GemCatalogScreen::GenerateTestData() + { + QVector result; + + GemInfo gem("EMotion FX", + "O3DE Foundation", + "EMFX is a real-time character animation system. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + (GemInfo::Android | GemInfo::iOS | GemInfo::macOS | GemInfo::Windows | GemInfo::Linux), + true); + gem.m_directoryLink = "C:/"; + gem.m_documentationLink = "http://www.amazon.com"; + gem.m_dependingGemUuids = QStringList({"EMotionFX", "Atom"}); + gem.m_conflictingGemUuids = QStringList({"Vegetation", "Camera", "ScriptCanvas", "CloudCanvas", "Networking"}); + gem.m_version = "v1.01"; + gem.m_lastUpdatedDate = "24th April 2021"; + gem.m_binarySizeInKB = 40; + gem.m_features = QStringList({"Animation", "Assets", "Physics"}); + result.push_back(gem); + + gem.m_name = "Atom"; + gem.m_creator = "O3DE Seattle"; + gem.m_summary = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + gem.m_platforms = (GemInfo::Android | GemInfo::Windows | GemInfo::Linux | GemInfo::macOS); + gem.m_isAdded = true; + gem.m_directoryLink = "C:/"; + gem.m_documentationLink = "https://aws.amazon.com/gametech/"; + gem.m_dependingGemUuids = QStringList({"EMotionFX", "Core", "AudioSystem", "Camera", "Particles"}); + gem.m_conflictingGemUuids = QStringList({"CloudCanvas", "NovaNet"}); + gem.m_version = "v2.31"; + gem.m_lastUpdatedDate = "24th November 2020"; + gem.m_features = QStringList({"Assets", "Rendering", "UI", "VR", "Debug", "Environment"}); + gem.m_binarySizeInKB = 2087; + result.push_back(gem); + + gem.m_name = "Physics"; + gem.m_creator = "O3DE London"; + gem.m_summary = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; + gem.m_platforms = (GemInfo::Android | GemInfo::Linux | GemInfo::macOS); + gem.m_isAdded = true; + gem.m_directoryLink = "C:/"; + gem.m_documentationLink = "https://aws.amazon.com/gametech/"; + gem.m_dependingGemUuids = QStringList({"GraphCanvas", "ExpressionEvaluation", "UI Lib", "Multiplayer", "GameStateSamples"}); + gem.m_conflictingGemUuids = QStringList({"Cloud Canvas", "EMotion FX", "Streaming", "MessagePopup", "Cloth", "Graph Canvas", "Twitch Integration"}); + gem.m_version = "v1.5.102145"; + gem.m_lastUpdatedDate = "1st January 2021"; + gem.m_binarySizeInKB = 2000000; + gem.m_features = QStringList({"Physics", "Gameplay", "Debug", "Assets"}); + result.push_back(gem); + + result.push_back(O3DE::ProjectManager::GemInfo("Certificate Manager", + "O3DE Irvine", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + GemInfo::Windows, + false)); + + result.push_back(O3DE::ProjectManager::GemInfo("Cloud Gem Framework", + "O3DE Seattle", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + GemInfo::iOS | GemInfo::Linux, + false)); + + result.push_back(O3DE::ProjectManager::GemInfo("Cloud Gem Core", + "O3DE Foundation", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + GemInfo::Android | GemInfo::Windows | GemInfo::Linux, + true)); + + result.push_back(O3DE::ProjectManager::GemInfo("Gestures", + "O3DE Foundation", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + GemInfo::Android | GemInfo::Windows | GemInfo::Linux, + false)); + + result.push_back(O3DE::ProjectManager::GemInfo("Effects System", + "O3DE Foundation", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + GemInfo::Android | GemInfo::Windows | GemInfo::Linux, + true)); + + result.push_back(O3DE::ProjectManager::GemInfo("Microphone", + "O3DE Foundation", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus euismod ligula vitae dui dictum, a sodales dolor luctus. Sed id elit dapibus, finibus neque sed, efficitur mi. Nam facilisis ligula at eleifend pellentesque. Praesent non ex consectetur, blandit tellus in, venenatis lacus. Duis nec neque in urna ullamcorper euismod id eu leo. Nam efficitur dolor sed odio vehicula venenatis. Suspendisse nec est non velit commodo cursus in sit amet dui. Ut bibendum nisl et libero hendrerit dapibus. Vestibulum ultrices ullamcorper urna, placerat porttitor est lobortis in. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer a magna ac tellus sollicitudin porttitor. Phasellus lobortis viverra justo id bibendum. Etiam ac pharetra risus. Nulla vitae justo nibh. Nulla viverra leo et molestie interdum. Duis sit amet bibendum nulla, sit amet vehicula augue.", + GemInfo::Android | GemInfo::Windows | GemInfo::Linux, + false)); + + return result; } ProjectManagerScreen GemCatalogScreen::GetScreenEnum() diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h index baa7af88b9..6a9c88d0f5 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.h @@ -14,6 +14,7 @@ #if !defined(Q_MOC_RUN) #include #include +#include #include #endif @@ -29,7 +30,10 @@ namespace O3DE::ProjectManager QString GetNextButtonText() override; private: + QVector GenerateTestData(); + GemListView* m_gemListView = nullptr; + GemInspector* m_gemInspector = nullptr; GemModel* m_gemModel = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp index 7ba4021205..729935fc8e 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp @@ -22,10 +22,33 @@ namespace O3DE::ProjectManager , m_isAdded(isAdded) { } - + bool GemInfo::IsValid() const { return !m_path.isEmpty() && !m_uuid.IsNull(); } + QString GemInfo::GetPlatformString(Platform platform) + { + switch (platform) + { + case O3DE::ProjectManager::GemInfo::Android: + return "Android"; + case O3DE::ProjectManager::GemInfo::iOS: + return "iOS"; + case O3DE::ProjectManager::GemInfo::Linux: + return "Linux"; + case O3DE::ProjectManager::GemInfo::macOS: + return "macOS"; + case O3DE::ProjectManager::GemInfo::Windows: + return "Windows"; + default: + return ""; + } + } + + bool GemInfo::IsPlatformSupported(Platform platform) const + { + return (m_platforms & platform); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index 098b67dbf5..7ee619702f 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -34,9 +34,11 @@ namespace O3DE::ProjectManager NumPlatforms = 5 }; Q_DECLARE_FLAGS(Platforms, Platform) + static QString GetPlatformString(Platform platform); GemInfo() = default; GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded); + bool IsPlatformSupported(Platform platform) const; bool IsValid() const; @@ -49,11 +51,13 @@ namespace O3DE::ProjectManager QString m_summary; Platforms m_platforms; QStringList m_features; + QString m_directoryLink; + QString m_documentationLink; QString m_version; QString m_lastUpdatedDate; - QString m_documentationUrl; - QVector m_dependingGemUuids; - QVector m_conflictingGemUuids; + int m_binarySizeInKB = 0; + QStringList m_dependingGemUuids; + QStringList m_conflictingGemUuids; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp new file mode 100644 index 0000000000..e7c682afd1 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -0,0 +1,176 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include + +namespace O3DE::ProjectManager +{ + GemInspector::GemInspector(GemModel* model, QWidget* parent) + : QScrollArea(parent) + , m_model(model) + { + setWidgetResizable(true); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + m_mainWidget = new QWidget(); + setWidget(m_mainWidget); + + m_mainLayout = new QVBoxLayout(); + m_mainLayout->setMargin(15); + m_mainLayout->setAlignment(Qt::AlignTop); + m_mainWidget->setLayout(m_mainLayout); + + InitMainWidget(); + + connect(m_model->GetSelectionModel(), &QItemSelectionModel::selectionChanged, this, &GemInspector::OnSelectionChanged); + Update({}); + } + + void GemInspector::OnSelectionChanged(const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected) + { + const QModelIndexList selectedIndices = selected.indexes(); + if (selectedIndices.empty()) + { + Update({}); + return; + } + + Update(selectedIndices[0]); + } + + void GemInspector::Update(const QModelIndex& modelIndex) + { + if (!modelIndex.isValid()) + { + m_mainWidget->hide(); + } + + m_nameLabel->setText(m_model->GetName(modelIndex)); + m_creatorLabel->setText(m_model->GetCreator(modelIndex)); + + m_summaryLabel->setText(m_model->GetSummary(modelIndex)); + m_summaryLabel->adjustSize(); + + m_directoryLinkLabel->SetUrl(m_model->GetDirectoryLink(modelIndex)); + m_documentationLinkLabel->SetUrl(m_model->GetDocLink(modelIndex)); + + // Depending and conflicting gems + m_dependingGems->Update("Depending Gems", "The following Gems will be automatically enabled with this Gem.", m_model->GetDependingGems(modelIndex)); + m_conflictingGems->Update("Conflicting Gems", "The following Gems will be automatically disabled with this Gem.", m_model->GetConflictingGems(modelIndex)); + + // Additional information + m_versionLabel->setText(QString("Gem Version: %1").arg(m_model->GetVersion(modelIndex))); + m_lastUpdatedLabel->setText(QString("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex))); + m_binarySizeLabel->setText(QString("Binary Size: %1 KB").arg(QString::number(m_model->GetBinarySizeInKB(modelIndex)))); + + m_mainWidget->adjustSize(); + m_mainWidget->show(); + } + + QLabel* GemInspector::CreateStyledLabel(QLayout* layout, int fontSize, const QString& colorCodeString) + { + QLabel* result = new QLabel(); + result->setStyleSheet(QString("font-size: %1pt; color: %2;").arg(QString::number(fontSize), colorCodeString)); + layout->addWidget(result); + return result; + } + + void GemInspector::InitMainWidget() + { + // Gem name, creator and summary + m_nameLabel = CreateStyledLabel(m_mainLayout, 17, s_headerColor); + m_creatorLabel = CreateStyledLabel(m_mainLayout, 12, s_creatorColor); + m_mainLayout->addSpacing(5); + + // TODO: QLabel seems to have issues determining the right sizeHint() for our font with the given font size. + // This results into squeezed elements in the layout in case the text is a little longer than a sentence. + m_summaryLabel = new QLabel();//CreateLabel(m_mainLayout, 12, s_textColor); + m_mainLayout->addWidget(m_summaryLabel); + m_summaryLabel->setWordWrap(true); + m_mainLayout->addSpacing(5); + + // Directory and documentation links + { + QHBoxLayout* linksHLayout = new QHBoxLayout(); + linksHLayout->setMargin(0); + m_mainLayout->addLayout(linksHLayout); + + QSpacerItem* spacerLeft = new QSpacerItem(0, 0, QSizePolicy::Expanding); + linksHLayout->addSpacerItem(spacerLeft); + + m_directoryLinkLabel = new LinkLabel("View in Directory"); + linksHLayout->addWidget(m_directoryLinkLabel); + linksHLayout->addWidget(new QLabel("|")); + m_documentationLinkLabel = new LinkLabel("Read Documentation"); + linksHLayout->addWidget(m_documentationLinkLabel); + + QSpacerItem* spacerRight = new QSpacerItem(0, 0, QSizePolicy::Expanding); + linksHLayout->addSpacerItem(spacerRight); + + m_mainLayout->addSpacing(8); + } + + // Separating line + QFrame* hLine = new QFrame(); + hLine->setFrameShape(QFrame::HLine); + hLine->setStyleSheet("color: #666666;"); + m_mainLayout->addWidget(hLine); + + m_mainLayout->addSpacing(10); + + // Depending and conflicting gems + m_dependingGems = new GemsSubWidget(); + m_mainLayout->addWidget(m_dependingGems); + m_mainLayout->addSpacing(20); + + m_conflictingGems = new GemsSubWidget(); + m_mainLayout->addWidget(m_conflictingGems); + m_mainLayout->addSpacing(20); + + // Additional information + QLabel* additionalInfoLabel = CreateStyledLabel(m_mainLayout, 14, s_headerColor); + additionalInfoLabel->setText("Additional Information"); + + m_versionLabel = CreateStyledLabel(m_mainLayout, 11, s_textColor); + m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 11, s_textColor); + m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 11, s_textColor); + } + + GemInspector::GemsSubWidget::GemsSubWidget(QWidget* parent) + : QWidget(parent) + { + m_layout = new QVBoxLayout(); + m_layout->setAlignment(Qt::AlignTop); + m_layout->setMargin(0); + setLayout(m_layout); + + m_titleLabel = GemInspector::CreateStyledLabel(m_layout, 15, s_headerColor); + m_textLabel = GemInspector::CreateStyledLabel(m_layout, 9, s_textColor); + m_textLabel->setWordWrap(true); + + m_tagWidget = new TagContainerWidget(); + m_layout->addWidget(m_tagWidget); + } + + void GemInspector::GemsSubWidget::Update(const QString& title, const QString& text, const QStringList& gemNames) + { + m_titleLabel->setText(title); + m_textLabel->setText(text); + m_tagWidget->Update(gemNames); + } +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h new file mode 100644 index 0000000000..69c065c81e --- /dev/null +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h @@ -0,0 +1,88 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#if !defined(Q_MOC_RUN) +#include +#include +#include +#include +#include +#include +#include +#endif + +QT_FORWARD_DECLARE_CLASS(QVBoxLayout) +QT_FORWARD_DECLARE_CLASS(QLabel) + +namespace O3DE::ProjectManager +{ + class GemInspector + : public QScrollArea + { + Q_OBJECT // AUTOMOC + + public: + explicit GemInspector(GemModel* model, QWidget* parent = nullptr); + ~GemInspector() = default; + + void Update(const QModelIndex& modelIndex); + static QLabel* CreateStyledLabel(QLayout* layout, int fontSize, const QString& colorCodeString); + + // Colors + inline constexpr static const char* s_headerColor = "#FFFFFF"; + inline constexpr static const char* s_textColor = "#DDDDDD"; + inline constexpr static const char* s_creatorColor = "#94D2FF"; + + private slots: + void OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + + private: + // Title, description and tag widget container used for the depending and conflicting gems + class GemsSubWidget + : public QWidget + { + public: + GemsSubWidget(QWidget* parent = nullptr); + void Update(const QString& title, const QString& text, const QStringList& gemNames); + + private: + QLabel* m_titleLabel = nullptr; + QLabel* m_textLabel = nullptr; + QVBoxLayout* m_layout = nullptr; + TagContainerWidget* m_tagWidget = nullptr; + }; + + void InitMainWidget(); + + GemModel* m_model = nullptr; + QWidget* m_mainWidget = nullptr; + QVBoxLayout* m_mainLayout = nullptr; + + // General info (top) section + QLabel* m_nameLabel = nullptr; + QLabel* m_creatorLabel = nullptr; + QLabel* m_summaryLabel = nullptr; + LinkLabel* m_directoryLinkLabel = nullptr; + LinkLabel* m_documentationLinkLabel = nullptr; + + // Depending and conflicting gems + GemsSubWidget* m_dependingGems = nullptr; + GemsSubWidget* m_conflictingGems = nullptr; + + // Additional information + QLabel* m_versionLabel = nullptr; + QLabel* m_lastUpdatedLabel = nullptr; + QLabel* m_binarySizeLabel = nullptr; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 27905fd4d2..1112c656f3 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -37,6 +37,16 @@ namespace O3DE::ProjectManager item->setData(gemInfo.m_summary, RoleSummary); item->setData(gemInfo.m_isAdded, RoleIsAdded); + item->setData(gemInfo.m_directoryLink, RoleDirectoryLink); + item->setData(gemInfo.m_documentationLink, RoleDocLink); + item->setData(gemInfo.m_dependingGemUuids, RoleDependingGems); + item->setData(gemInfo.m_conflictingGemUuids, RoleConflictingGems); + item->setData(gemInfo.m_version, RoleVersion); + item->setData(gemInfo.m_lastUpdatedDate, RoleLastUpdated); + item->setData(gemInfo.m_binarySizeInKB, RoleBinarySize); + + item->setData(gemInfo.m_features, RoleFeatures); + appendRow(item); } @@ -45,28 +55,68 @@ namespace O3DE::ProjectManager clear(); } - QString GemModel::GetName(const QModelIndex& modelIndex) const + QString GemModel::GetName(const QModelIndex& modelIndex) { return modelIndex.data(RoleName).toString(); } - QString GemModel::GetCreator(const QModelIndex& modelIndex) const + QString GemModel::GetCreator(const QModelIndex& modelIndex) { return modelIndex.data(RoleCreator).toString(); } - GemInfo::Platforms GemModel::GetPlatforms(const QModelIndex& modelIndex) const + GemInfo::Platforms GemModel::GetPlatforms(const QModelIndex& modelIndex) { return static_cast(modelIndex.data(RolePlatforms).toInt()); } - QString GemModel::GetSummary(const QModelIndex& modelIndex) const + QString GemModel::GetSummary(const QModelIndex& modelIndex) { return modelIndex.data(RoleSummary).toString(); } - bool GemModel::IsAdded(const QModelIndex& modelIndex) const + bool GemModel::IsAdded(const QModelIndex& modelIndex) { return modelIndex.data(RoleIsAdded).toBool(); } + + QString GemModel::GetDirectoryLink(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleDirectoryLink).toString(); + } + + QString GemModel::GetDocLink(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleDocLink).toString(); + } + + QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleDependingGems).toStringList(); + } + + QStringList GemModel::GetConflictingGems(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleConflictingGems).toStringList(); + } + + QString GemModel::GetVersion(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleVersion).toString(); + } + + QString GemModel::GetLastUpdated(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleLastUpdated).toString(); + } + + int GemModel::GetBinarySizeInKB(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleBinarySize).toInt(); + } + + QStringList GemModel::GetFeatures(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleFeatures).toStringList(); + } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 4ce9de32fc..fba65e7009 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -14,6 +14,7 @@ #if !defined(Q_MOC_RUN) #include "GemInfo.h" +#include #include #include #endif @@ -32,11 +33,19 @@ namespace O3DE::ProjectManager void AddGem(const GemInfo& gemInfo); void Clear(); - QString GetName(const QModelIndex& modelIndex) const; - QString GetCreator(const QModelIndex& modelIndex) const; - GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex) const; - QString GetSummary(const QModelIndex& modelIndex) const; - bool IsAdded(const QModelIndex& modelIndex) const; + static QString GetName(const QModelIndex& modelIndex); + static QString GetCreator(const QModelIndex& modelIndex); + static GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex); + static QString GetSummary(const QModelIndex& modelIndex); + static bool IsAdded(const QModelIndex& modelIndex); + static QString GetDirectoryLink(const QModelIndex& modelIndex); + static QString GetDocLink(const QModelIndex& modelIndex); + static QStringList GetDependingGems(const QModelIndex& modelIndex); + static QStringList GetConflictingGems(const QModelIndex& modelIndex); + static QString GetVersion(const QModelIndex& modelIndex); + static QString GetLastUpdated(const QModelIndex& modelIndex); + static int GetBinarySizeInKB(const QModelIndex& modelIndex); + static QStringList GetFeatures(const QModelIndex& modelIndex); private: enum UserRole @@ -45,7 +54,15 @@ namespace O3DE::ProjectManager RoleCreator, RolePlatforms, RoleSummary, - RoleIsAdded + RoleIsAdded, + RoleDirectoryLink, + RoleDocLink, + RoleDependingGems, + RoleConflictingGems, + RoleVersion, + RoleLastUpdated, + RoleBinarySize, + RoleFeatures, }; QItemSelectionModel* m_selectionModel = nullptr; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index cc5348fd22..b6945e012e 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -245,7 +245,7 @@ namespace O3DE::ProjectManager { for (auto dependency : data["Dependencies"]) { - gemInfo.m_dependingGemUuids.push_back(AZ::Uuid(Py_To_String(dependency["Uuid"]))); + gemInfo.m_dependingGemUuids.push_back(Py_To_String(dependency["Uuid"])); } } if (data.contains("Tags")) diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 3249d293ad..9ffdb6029d 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -54,6 +54,8 @@ set(FILES Source/GemCatalog/GemCatalogScreen.cpp Source/GemCatalog/GemInfo.h Source/GemCatalog/GemInfo.cpp + Source/GemCatalog/GemInspector.h + Source/GemCatalog/GemInspector.cpp Source/GemCatalog/GemItemDelegate.h Source/GemCatalog/GemItemDelegate.cpp Source/GemCatalog/GemListView.h