Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,25 @@
/*
* 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 "AzToolsFramework_precompiled.h"
#include <AzToolsFramework/ViewportUi/Button.h>
#include <AzToolsFramework/ViewportUi/Cluster.h>
namespace AzToolsFramework::ViewportUi::Internal
{
Button::Button(AZStd::string icon, ButtonId buttonId)
: m_icon(AZStd::move(icon))
, m_buttonId(buttonId)
{
}
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,37 @@
/*
* 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 <AzToolsFramework/ViewportUi/Cluster.h>
#include <AzToolsFramework/ViewportUi/ViewportUiRequestBus.h>
namespace AzToolsFramework::ViewportUi::Internal
{
//! Data class for holding button settings.
class Button
{
public:
enum class State
{
Selected,
Deselected
};
explicit Button(AZStd::string icon, ButtonId buttonId);
~Button() = default;
AZStd::string m_icon; //!< The icon for this button, string path to an image.
State m_state = State::Deselected;
ButtonId m_buttonId;
};
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,91 @@
/*
* 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 <AzToolsFramework/ViewportUi/Button.h>
#include <AzToolsFramework/ViewportUi/Cluster.h>
namespace AzToolsFramework::ViewportUi::Internal
{
Cluster::Cluster()
: m_buttons()
, m_buttonTriggeredEvent()
{
}
void Cluster::SetViewportUiElementId(const ViewportUiElementId id)
{
m_viewportUiId = id;
}
ViewportUiElementId Cluster::GetViewportUiElementId() const
{
return m_viewportUiId;
}
void Cluster::SetClusterId(const ClusterId clusterId)
{
m_clusterId = clusterId;
}
ClusterId Cluster::GetClusterId() const
{
return m_clusterId;
}
void Cluster::SetHighlightedButton(ButtonId buttonId)
{
if (auto buttonEntry = m_buttons.find(buttonId); buttonEntry != m_buttons.end())
{
for (auto& button : m_buttons)
{
button.second->m_state = Button::State::Deselected;
}
buttonEntry->second->m_state = Button::State::Selected;
}
}
ButtonId Cluster::AddButton(const AZStd::string& icon)
{
auto buttonId = ButtonId(m_buttons.size() + 1);
m_buttons.insert({buttonId, AZStd::make_unique<Button>(icon, buttonId)});
return buttonId;
}
Button* Cluster::GetButton(ButtonId buttonId)
{
if (auto buttonEntry = m_buttons.find(buttonId); buttonEntry != m_buttons.end())
{
return buttonEntry->second.get();
}
return nullptr;
}
AZStd::vector<Button*> Cluster::GetButtons()
{
auto buttons = AZStd::vector<Button*>();
for (const auto& button : m_buttons)
{
buttons.push_back(button.second.get());
}
return buttons;
}
void Cluster::ConnectEventHandler(AZ::Event<ButtonId>::Handler& handler) {
handler.Connect(m_buttonTriggeredEvent);
}
void Cluster::PressButton(ButtonId buttonId)
{
m_buttonTriggeredEvent.Signal(buttonId);
}
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,50 @@
#pragma once
/*
* 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 <AzToolsFramework/ViewportUi/ViewportUiRequestBus.h>
namespace AzToolsFramework::ViewportUi::Internal
{
class Button;
//! Data class for a cluster on the Viewport UI. A cluster is defined as a group of buttons with icons
//! each of which can be clicked to trigger an event e.g. toggling between modes.
class Cluster
{
public:
Cluster();
~Cluster() = default;
void SetHighlightedButton(ButtonId buttonId);
void SetViewportUiElementId(ViewportUiElementId id);
ViewportUiElementId GetViewportUiElementId() const;
void SetClusterId(ClusterId id);
ClusterId GetClusterId() const;
ButtonId AddButton(const AZStd::string& icon);
Button* GetButton(ButtonId buttonId);
AZStd::vector<Button*> GetButtons();
void ConnectEventHandler(AZ::Event<ButtonId>::Handler& handler);
void PressButton(ButtonId buttonId);
private:
AZ::Event<ButtonId> m_buttonTriggeredEvent;
ViewportUiElementId m_viewportUiId;
ClusterId m_clusterId;
AZStd::unordered_map<ButtonId, AZStd::unique_ptr<Button>> m_buttons;
};
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,30 @@
/*
* 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 "TextField.h"
namespace AzToolsFramework::ViewportUi::Internal
{
TextField::TextField(
const AZStd::string& labelText, const AZStd::string& fieldText,
TextFieldValidationType validationType)
: m_labelText(labelText)
, m_fieldText(fieldText)
, m_validationType(validationType)
{
}
void TextField::ConnectEventHandler(AZ::Event<AZStd::string>::Handler& handler)
{
handler.Connect(m_textEditedEvent);
}
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,40 @@
/*
* 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 <AzCore/std/string/string.h>
#include <AzToolsFramework/ViewportUi/ViewportUiRequestBus.h>
namespace AzToolsFramework::ViewportUi::Internal
{
//! Data class for a string label and text edit field.
//! E.g.: ScaleX [_____].
class TextField
{
public:
TextField(
const AZStd::string& labelText = "", const AZStd::string& fieldText = "",
TextFieldValidationType validationType = TextFieldValidationType::String);
~TextField() = default;
void ConnectEventHandler(AZ::Event<AZStd::string>::Handler& handler);
//! Default text for the text field. Will be cast to same type as m_validationType.
AZStd::string m_fieldText;
AZStd::string m_labelText;
TextFieldValidationType m_validationType; //<! The type of validator for this text edit.
TextFieldId m_textFieldId;
ViewportUiElementId m_viewportId;
AZ::Event<AZStd::string> m_textEditedEvent;
};
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,114 @@
/*
* 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 "AzToolsFramework_precompiled.h"
#include <AzToolsFramework/ViewportUi/Cluster.h>
#include <AzToolsFramework/ViewportUi/ViewportUiCluster.h>
namespace AzToolsFramework::ViewportUi::Internal
{
ViewportUiCluster::ViewportUiCluster(AZStd::shared_ptr<Cluster> cluster)
: QToolBar(nullptr)
, m_cluster(cluster)
{
setOrientation(Qt::Orientation::Vertical);
setStyleSheet("background: black;");
const AZStd::vector<Button*> buttons = cluster->GetButtons();
for (auto button : buttons)
{
RegisterButton(button);
}
}
void ViewportUiCluster::RegisterButton(Button* button)
{
QAction* action = new QAction();
action->setCheckable(true);
action->setIcon(QIcon(QString(button->m_icon.c_str())));
AddClusterAction(
action,
[this, button]() {
m_cluster->PressButton(button->m_buttonId);
},
[button](QAction* action) {
action->setChecked(button->m_state == Button::State::Selected);
});
m_buttonActionMap.insert({ button->m_buttonId, action });
}
void ViewportUiCluster::RemoveButton(ButtonId buttonId)
{
if (auto actionEntry = m_buttonActionMap.find(buttonId);
actionEntry != m_buttonActionMap.end())
{
auto action = actionEntry->second;
RemoveClusterAction(action);
m_buttonActionMap.erase(buttonId);
}
}
void ViewportUiCluster::AddClusterAction(
QAction* action, const AZStd::function<void()>& callback,
const AZStd::function<void(QAction*)>& updateCallback)
{
if (!action)
{
return;
}
// set hover to true by default
action->setProperty("IconHasHoverEffect", true);
// add the action
addAction(action);
// resize to fit new action with minimum extra space
resize(minimumSizeHint());
// connect the callback if provided
if (callback)
{
QObject::connect(action, &QAction::triggered, action, callback);
}
// register the action
m_widgetCallbacks.AddWidget(action, [updateCallback](QPointer<QObject> object)
{
updateCallback(static_cast<QAction*>(object.data()));
});
}
void ViewportUiCluster::RemoveClusterAction(QAction* action)
{
// remove the action from the toolbar
removeAction(action);
// deregister from the widget manager
m_widgetCallbacks.RemoveWidget(action);
// resize to fit new area with minimum extra space
resize(minimumSizeHint());
}
void ViewportUiCluster::Update()
{
m_widgetCallbacks.Update();
}
ViewportUiWidgetCallbacks ViewportUiCluster::GetWidgetCallbacks()
{
return m_widgetCallbacks;
}
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +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 <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzToolsFramework/ViewportUi/Button.h>
#include <AzToolsFramework/ViewportUi/ViewportUiWidgetCallbacks.h>
#include <QToolBar>
class Cluster;
namespace AzToolsFramework::ViewportUi::Internal
{
//! Helper class to make clusters (toolbars) for display in Viewport UI.
class ViewportUiCluster
: public QToolBar
{
Q_OBJECT
public:
ViewportUiCluster(AZStd::shared_ptr<Cluster> cluster);
~ViewportUiCluster() = default;
//! Adds a new button to the cluster.
void RegisterButton(Button* button);
//! Removes a button from the cluster.
void RemoveButton(ButtonId buttonId);
//! Updates all registered actions.
void Update();
//! Returns the widget manager.
ViewportUiWidgetCallbacks GetWidgetCallbacks();
private:
//! Adds an action to the Viewport UI Cluster.
void AddClusterAction(
QAction* action, const AZStd::function<void()>& callback = {},
const AZStd::function<void(QAction*)>& updateCallback = {});
//! Removes an action from the Viewport UI Cluster.
void RemoveClusterAction(QAction* action);
AZStd::shared_ptr<Cluster> m_cluster; //!< Data structure which the cluster will be displaying to the Viewport UI.
AZStd::unordered_map<ButtonId, QPointer<QAction>> m_buttonActionMap; //!< Map for buttons to their corresponding actions.
ViewportUiWidgetCallbacks m_widgetCallbacks; //!< Registers actions and manages updates.
};
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,420 @@
/*
* 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 "AzToolsFramework_precompiled.h"
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
#include <AzToolsFramework/ViewportUi/ViewportUiDisplay.h>
#include <AzToolsFramework/ViewportUi/ViewportUiCluster.h>
#include <AzToolsFramework/ViewportUi/ViewportUiTextField.h>
#include <QWidget>
namespace AzToolsFramework::ViewportUi::Internal
{
// margin for the Viewport UI Overlay in pixels
const static int ViewportUiOverlayMargin = 5;
const static int HighlightBorderSize = 5;
const static int TopHighlightBorderSize = 25;
const static char* HighlightBorderColor = "#44B2F8";
static void UnparentWidgets(ViewportUiElementIdInfoLookup& viewportUiElementIdInfoLookup)
{
for (auto& element : viewportUiElementIdInfoLookup)
{
if (element.second.m_widget)
{
element.second.m_widget->setParent(nullptr);
}
}
}
ViewportUiDisplay::ViewportUiDisplay(QWidget* parent, QWidget* renderOverlay)
: m_renderOverlay(renderOverlay)
, m_uiMainWindow(parent)
, m_uiOverlay(parent)
, m_fullScreenLayout(&m_uiOverlay)
, m_uiOverlayLayout()
, m_componentModeBorderText(&m_uiOverlay)
{
}
ViewportUiDisplay::~ViewportUiDisplay()
{
UnparentWidgets(m_viewportUiElements);
}
void ViewportUiDisplay::AddCluster(AZStd::shared_ptr<Cluster> cluster)
{
if (!cluster.get())
{
return;
}
auto viewportUiCluster = AZStd::make_shared<ViewportUiCluster>(cluster);
auto id = AddViewportUiElement(viewportUiCluster);
cluster->SetViewportUiElementId(id);
PositionViewportUiElementAnchored(id, Qt::AlignTop | Qt::AlignLeft);
}
void ViewportUiDisplay::AddClusterButton(
const ViewportUiElementId clusterId, Button* button)
{
if (auto viewportUiCluster = qobject_cast<ViewportUiCluster*>(GetViewportUiElement(clusterId).get()))
{
viewportUiCluster->RegisterButton(button);
}
}
void ViewportUiDisplay::RemoveClusterButton(ViewportUiElementId clusterId, ButtonId buttonId)
{
if (auto cluster = qobject_cast<ViewportUiCluster*>(GetViewportUiElement(clusterId).get()))
{
cluster->RemoveButton(buttonId);
}
}
void ViewportUiDisplay::UpdateCluster(ViewportUiElementId clusterId)
{
if (auto cluster = qobject_cast<ViewportUiCluster*>(GetViewportUiElement(clusterId).get()))
{
cluster->Update();
}
}
void ViewportUiDisplay::AddTextField(AZStd::shared_ptr<TextField> textField)
{
if (!textField.get())
{
return;
}
auto viewportUiTextField = AZStd::make_shared<ViewportUiTextField>(textField);
auto id = AddViewportUiElement(viewportUiTextField);
textField->m_viewportId = id;
PositionViewportUiElementAnchored(id, Qt::AlignTop | Qt::AlignLeft);
}
void ViewportUiDisplay::UpdateTextField(const ViewportUiElementId textFieldId)
{
if (auto textField = qobject_cast<ViewportUiTextField*>(GetViewportUiElement(textFieldId).get()))
{
textField->Update();
}
}
void ViewportUiDisplay::Update()
{
for (const auto& element : m_viewportUiElements)
{
const ViewportUiElementInfo& elementInfo = element.second;
if (!elementInfo.m_anchored)
{
const auto screenPoint = AzFramework::WorldToScreen(
elementInfo.m_worldPosition, AzToolsFramework::GetCameraState(m_viewportId));
elementInfo.m_widget->move(screenPoint.m_x, screenPoint.m_y);
}
}
PositionUiOverlayOverRenderViewport();
}
ViewportUiElementId ViewportUiDisplay::AddViewportUiElement(AZStd::shared_ptr<QWidget> widget)
{
if (!widget)
{
return InvalidViewportUiElementId;
}
ViewportUiElementId newId = ViewportUiElementId(++m_numViewportElements);
ViewportUiElementInfo newElement{ widget, newId, true };
m_viewportUiElements.insert({ newId, newElement });
SetUiOverlayContents(widget.get());
return newId;
}
AZStd::shared_ptr<QWidget> ViewportUiDisplay::GetViewportUiElement(ViewportUiElementId elementId)
{
auto element = m_viewportUiElements.find(elementId);
if (element != m_viewportUiElements.end())
{
return element->second.m_widget;
}
return nullptr;
}
ViewportUiElementId ViewportUiDisplay::GetViewportUiElementId(QPointer<QWidget> widget)
{
if (auto element = AZStd::find_if(
m_viewportUiElements.begin(), m_viewportUiElements.end(),
[widget](const auto& it) { return it.second.m_widget.get() == widget; });
element != m_viewportUiElements.end())
{
return element->second.m_viewportUiElementId;
}
return InvalidViewportUiElementId;
}
void ViewportUiDisplay::RemoveViewportUiElement(ViewportUiElementId elementId)
{
AZ_Assert(elementId != AzToolsFramework::ViewportUi::InvalidViewportUiElementId,
"Tried to remove a Viewport UI element using an invalid or removed ViewportUiElementId.");
auto viewportUiMapElement = m_viewportUiElements.find(elementId);
if (viewportUiMapElement != m_viewportUiElements.end())
{
viewportUiMapElement->second.m_widget->setVisible(false);
viewportUiMapElement->second.m_widget->setParent(nullptr);
m_viewportUiElements.erase(viewportUiMapElement);
}
}
bool ViewportUiDisplay::UiDisplayEnabled() const
{
return m_renderOverlay && m_renderOverlay->isVisible();
}
void ViewportUiDisplay::ShowViewportUiElement(ViewportUiElementId elementId)
{
if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId);
element.m_widget)
{
element.m_widget->setVisible(true);
}
}
void ViewportUiDisplay::HideViewportUiElement(ViewportUiElementId elementId)
{
if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId);
element.m_widget && UiDisplayEnabled())
{
element.m_widget->setVisible(false);
}
}
bool ViewportUiDisplay::IsViewportUiElementVisible(ViewportUiElementId elementId)
{
if (ViewportUiElementInfo element = GetViewportUiElementInfo(elementId);
element.m_widget)
{
return element.IsValid() && element.m_widget->isVisible();
}
return false;
}
void ViewportUiDisplay::CreateComponentModeBorder(const AZStd::string& borderTitle)
{
AZStd::string styleSheet = AZStd::string::format(
"border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize,
HighlightBorderColor, TopHighlightBorderSize, HighlightBorderColor);
m_uiOverlay.setStyleSheet(styleSheet.c_str());
m_uiOverlayLayout.setContentsMargins(HighlightBorderSize + ViewportUiOverlayMargin,
TopHighlightBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin,
HighlightBorderSize + ViewportUiOverlayMargin);
m_componentModeBorderText.setVisible(true);
m_componentModeBorderText.setText(borderTitle.c_str());
}
void ViewportUiDisplay::RemoveComponentModeBorder()
{
m_componentModeBorderText.setVisible(false);
m_uiOverlay.setStyleSheet("border: none;");
m_uiOverlayLayout.setMargin(ViewportUiOverlayMargin);
}
void ViewportUiDisplay::PositionViewportUiElementFromWorldSpace(ViewportUiElementId elementId, const AZ::Vector3& pos)
{
auto viewportUiMapElement = m_viewportUiElements.find(elementId);
if (viewportUiMapElement != m_viewportUiElements.end() &&
viewportUiMapElement->second.m_widget)
{
viewportUiMapElement->second.m_anchored = false;
viewportUiMapElement->second.m_worldPosition = pos;
SetUiOverlayContents(viewportUiMapElement->second.m_widget.get());
}
}
void ViewportUiDisplay::PositionViewportUiElementAnchored(ViewportUiElementId elementId, const Qt::Alignment alignment)
{
auto viewportUiMapElement = m_viewportUiElements.find(elementId);
if (viewportUiMapElement != m_viewportUiElements.end() &&
viewportUiMapElement->second.m_widget)
{
viewportUiMapElement->second.m_anchored = true;
SetUiOverlayContentsAnchored(viewportUiMapElement->second.m_widget.get(), alignment);
}
}
void ViewportUiDisplay::AddMaximumSizeViewportUiElement(QPointer<QWidget> widget)
{
if (widget)
{
return;
}
if (m_fullScreenWidget)
{
AZ_Warning(
"ViewportUi", false,
"Attaching a maximum size element when one already exists. Removing the previously attached element.");
RemoveViewportUiElement(GetViewportUiElementId(m_fullScreenWidget));
}
widget->setAttribute(Qt::WA_ShowWithoutActivating);
widget->setParent(&m_uiOverlay);
m_fullScreenWidget = widget;
m_fullScreenLayout.addWidget(m_fullScreenWidget, 0, 0, 1, 1);
m_renderOverlay->setFocus();
}
// disables system background for widget and gives a transparent background
static void ConfigureWidgetForViewportUi(QPointer<QWidget> widget)
{
// no background for the widget else each set of buttons/textfields/etc would have a black box around them
SetTransparentBackground(widget);
widget->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
}
void ViewportUiDisplay::InitializeUiOverlay()
{
m_uiMainWindow.setObjectName(m_uiMainWindow.windowTitle());
ConfigureWidgetForViewportUi(&m_uiMainWindow);
m_uiMainWindow.setVisible(false);
m_uiOverlay.setObjectName(m_uiOverlay.windowTitle());
m_uiMainWindow.setCentralWidget(&m_uiOverlay);
m_uiOverlay.setVisible(false);
// remove any spacing and margins from the UI Overlay Layout
m_fullScreenLayout.setSpacing(0);
m_fullScreenLayout.setContentsMargins(0, 0, 0, 0);
m_fullScreenLayout.addLayout(&m_uiOverlayLayout, 0, 0, 1, 1);
m_uiOverlayLayout.setMargin(ViewportUiOverlayMargin);
// format the label which will appear on top of the highlight border
AZStd::string styleSheet = AZStd::string::format(
"background-color: %s; border: none;", HighlightBorderColor);
m_componentModeBorderText.setStyleSheet(styleSheet.c_str());
m_componentModeBorderText.setFixedHeight(TopHighlightBorderSize);
m_componentModeBorderText.setVisible(false);
m_fullScreenLayout.addWidget(&m_componentModeBorderText, 0, 0, Qt::AlignTop | Qt::AlignHCenter);
}
void ViewportUiDisplay::PrepareWidgetForViewportUi(QPointer<QWidget> widget)
{
widget->setAttribute(Qt::WA_ShowWithoutActivating);
widget->setParent(&m_uiOverlay);
widget->setStyleSheet("border: none;");
}
void ViewportUiDisplay::SetUiOverlayContents(QPointer<QWidget> widget)
{
if (!widget)
{
return;
}
PrepareWidgetForViewportUi(widget);
m_renderOverlay->setFocus();
}
void ViewportUiDisplay::SetUiOverlayContentsAnchored(QPointer<QWidget> widget, Qt::Alignment alignment)
{
if (!widget)
{
return;
}
PrepareWidgetForViewportUi(widget);
m_uiOverlayLayout.AddAnchoredWidget(widget, alignment);
m_renderOverlay->setFocus();
}
void ViewportUiDisplay::UpdateUiOverlayGeometry()
{
// add the component mode border region if visible
QRegion region;
if (m_componentModeBorderText.isVisible())
{
// get the border region by taking the entire region and subtracting the non-border area
region += m_uiOverlay.rect();
region -= QRect(
QPoint(
m_uiOverlay.rect().left() + HighlightBorderSize,
m_uiOverlay.rect().top() + TopHighlightBorderSize),
QPoint(
m_uiOverlay.rect().right() - HighlightBorderSize,
m_uiOverlay.rect().bottom() - HighlightBorderSize)
);
}
// add all children widget regions
region += m_uiOverlay.childrenRegion();
// set viewport ui visibility depending on if elements are present
if (region.isEmpty() || !UiDisplayEnabled())
{
m_uiMainWindow.setVisible(false);
m_uiOverlay.setVisible(false);
}
else
{
m_uiMainWindow.setVisible(true);
m_uiOverlay.setVisible(true);
}
m_uiMainWindow.setMask(region);
}
void ViewportUiDisplay::PositionUiOverlayOverRenderViewport()
{
QPoint offset = m_renderOverlay->mapToGlobal(QPoint());
m_uiMainWindow.move(offset);
m_uiOverlay.setFixedSize(m_renderOverlay->width(), m_renderOverlay->height());
UpdateUiOverlayGeometry();
}
ViewportUiElementInfo ViewportUiDisplay::GetViewportUiElementInfo(const ViewportUiElementId elementId)
{
if (auto element = m_viewportUiElements.find(elementId);
element != m_viewportUiElements.end())
{
return element->second;
}
return ViewportUiElementInfo{ nullptr, InvalidViewportUiElementId, false };
}
const QMainWindow* ViewportUiDisplay::GetUiMainWindow() const
{
return &m_uiMainWindow;
}
const QWidget* ViewportUiDisplay::GetUiOverlay() const
{
return &m_uiOverlay;
}
const QGridLayout* ViewportUiDisplay::GetUiOverlayLayout() const
{
return &m_uiOverlayLayout;
}
void SetTransparentBackground(QWidget * widget)
{
widget->setAttribute(Qt::WA_TranslucentBackground);
widget->setAutoFillBackground(false);
}
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,121 @@
/*
* 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 <AzToolsFramework/ViewportUi/Button.h>
#include <AzToolsFramework/ViewportUi/Cluster.h>
#include <AzToolsFramework/ViewportUi/TextField.h>
#include <AzToolsFramework/ViewportUi/ViewportUiRequestBus.h>
#include <AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h>
#include <QMainWindow>
#include <QPointer>
#include <QLabel>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <QGridLayout>
AZ_POP_DISABLE_WARNING
class QPoint;
namespace AzToolsFramework::ViewportUi::Internal
{
//! Used to track info for each widget in the Viewport UI.
struct ViewportUiElementInfo
{
AZStd::shared_ptr<QWidget> m_widget; //<! Reference to the widget.
ViewportUiElementId m_viewportUiElementId; //<! Corresponding ViewportUiElementId of the widget.
bool m_anchored = true; //<! Whether the widget is anchored to one position or moves with camera/entity.
AZ::Vector3 m_worldPosition; //<! If not anchored, use this to project widget position to screen space.
bool IsValid() const
{
return m_viewportUiElementId != InvalidViewportUiElementId;
}
};
using ViewportUiElementIdInfoLookup = AZStd::unordered_map<ViewportUiElementId, ViewportUiElementInfo>;
//! Helper function to give a widget a transparent background
void SetTransparentBackground(QWidget* widget);
//! Creates a transparent widget over a viewport render overlay, and adds/manages other Qt widgets
//! to display on top of the viewport.
class ViewportUiDisplay
{
public:
ViewportUiDisplay(QWidget* parent, QWidget* renderOverlay);
~ViewportUiDisplay();
void AddCluster(AZStd::shared_ptr<Cluster> cluster);
void AddClusterButton(ViewportUiElementId clusterId, Button* button);
void RemoveClusterButton(ViewportUiElementId clusterId, ButtonId buttonId);
void UpdateCluster(const ViewportUiElementId clusterId);
void AddTextField(AZStd::shared_ptr<TextField> textField);
void UpdateTextField(ViewportUiElementId textFieldId);
//! After removing, can no longer be accessed by its ViewportUiElementId unless it is re-added.
void RemoveViewportUiElement(ViewportUiElementId elementId);
//! Moves the Viewport UI over the Render Overlay, projects new positions of non-anchored elements,
//! and sets Viewport UI geometry to include only areas populated by Viewport UI Elements.
void Update();
const QMainWindow* GetUiMainWindow() const;
const QWidget* GetUiOverlay() const;
const QGridLayout* GetUiOverlayLayout() const;
//! Initializes UI main window and overlay by setting attributes such as transparency and visibility.
void InitializeUiOverlay();
void ShowViewportUiElement(ViewportUiElementId elementId);
void HideViewportUiElement(ViewportUiElementId elementId);
AZStd::shared_ptr<QWidget> GetViewportUiElement(ViewportUiElementId elementId);
bool IsViewportUiElementVisible(ViewportUiElementId elementId);
void CreateComponentModeBorder(const AZStd::string& borderTitle);
void RemoveComponentModeBorder();
private:
void PrepareWidgetForViewportUi(QPointer<QWidget> widget);
ViewportUiElementId AddViewportUiElement(AZStd::shared_ptr<QWidget> widget);
ViewportUiElementId GetViewportUiElementId(QPointer<QWidget> widget);
void AddMaximumSizeViewportUiElement(QPointer<QWidget> widget);
void PositionViewportUiElementFromWorldSpace(ViewportUiElementId elementId, const AZ::Vector3& pos);
void PositionViewportUiElementAnchored(ViewportUiElementId elementId, const Qt::Alignment alignment);
void PositionUiOverlayOverRenderViewport();
bool UiDisplayEnabled() const;
void SetUiOverlayContents(QPointer<QWidget> widget);
void SetUiOverlayContentsAnchored(QPointer<QWidget>, Qt::Alignment aligment);
void UpdateUiOverlayGeometry();
ViewportUiElementInfo GetViewportUiElementInfo(const ViewportUiElementId elementId);
QMainWindow m_uiMainWindow; //<! The window which contains the UI Overlay.
QWidget m_uiOverlay; //<! The UI Overlay which displays Viewport UI Elements.
QGridLayout m_fullScreenLayout; //<! The layout which extends across the full screen.
ViewportUiDisplayLayout m_uiOverlayLayout; //<! The layout used for optionally anchoring Viewport UI Elements.
QLabel m_componentModeBorderText; //<! The text used for the Component Mode border.
QWidget* m_renderOverlay;
QPointer<QWidget> m_fullScreenWidget; //<! Reference to the widget attached to m_fullScreenLayout if any.
int m_viewportId;
int64_t m_numViewportElements = 0;
ViewportUiElementIdInfoLookup m_viewportUiElements;
};
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,73 @@
/*
* 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 "AzToolsFramework_precompiled.h"
#include <AzCore/Console/IConsole.h>
#include <AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h>
namespace AzToolsFramework::ViewportUi::Internal
{
AZ_CVAR(
int, ViewportUiDisplayLayoutSpacing, 5, nullptr, AZ::ConsoleFunctorFlags::Null,
"The spacing between elements attached to the Viewport UI Display Layout");
ViewportUiDisplayLayout::ViewportUiDisplayLayout(QWidget* parent)
: QGridLayout(parent)
{
// set margins and spacing for internal contents
setContentsMargins(0, 0, 0, 0);
setSpacing(ViewportUiDisplayLayoutSpacing);
// create a 3x2 map of sub layouts which will stack widgets according to their mapped alignment
m_internalLayouts = AZStd::unordered_map<Qt::Alignment, QBoxLayout*> {
CreateSubLayout(new QHBoxLayout(), 0, 0, Qt::AlignTop | Qt::AlignLeft),
CreateSubLayout(new QHBoxLayout(), 1, 0, Qt::AlignBottom | Qt::AlignLeft),
CreateSubLayout(new QVBoxLayout(), 0, 1, Qt::AlignTop),
CreateSubLayout(new QHBoxLayout(), 1, 1, Qt::AlignBottom),
CreateSubLayout(new QVBoxLayout(), 0, 2, Qt::AlignTop | Qt::AlignRight),
CreateSubLayout(new QHBoxLayout(), 1, 2, Qt::AlignBottom | Qt::AlignRight),
};
}
void ViewportUiDisplayLayout::AddAnchoredWidget(QPointer<QWidget> widget, const Qt::Alignment alignment)
{
if (!widget)
{
return;
}
// find the corresponding sub layout for the alignment and add the widget
if (auto layoutForAlignment = m_internalLayouts.find(alignment);
layoutForAlignment != m_internalLayouts.end())
{
// place the widget before the invisible spacer
// spacer must be last item in layout to not interfere with positioning
int index = layoutForAlignment->second->count() - 1;
layoutForAlignment->second->insertWidget(index, widget);
}
}
AZStd::pair<Qt::Alignment, QBoxLayout*> ViewportUiDisplayLayout::CreateSubLayout(
QBoxLayout* layout, const int row, const int column, const Qt::Alignment alignment)
{
layout->setAlignment(alignment);
// add an invisible spacer (stretch) to occupy empty space
// without this, alignment and resizing within the sublayouts becomes difficult
layout->addStretch(1);
addLayout(layout, row, column, /*rowSpan=*/ 1, /*colSpan=*/ 1, alignment);
return { alignment, layout };
}
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,41 @@
/*
* 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 <AzCore/std/containers/unordered_map.h>
#include <QGridLayout>
#include <QBoxLayout>
#include <QPointer>
namespace AzToolsFramework::ViewportUi::Internal
{
//! QGridLayout implementation that uses a grid of QVBox/QHBoxLayouts internally to stack widgets.
class ViewportUiDisplayLayout : public QGridLayout
{
public:
explicit ViewportUiDisplayLayout(QWidget* parent = nullptr);
~ViewportUiDisplayLayout() = default;
//! Add a QWidget to the corresponding internal sub-layout.
void AddAnchoredWidget(QPointer<QWidget> widget, Qt::Alignment alignment);
private:
//! Create a sub-layout to add to the grid of layouts.
//! @return A pair of the new layout along with its alignment on the grid.
AZStd::pair<Qt::Alignment, QBoxLayout*> CreateSubLayout(
QBoxLayout* layout, int row, int column, Qt::Alignment alignment);
//! A mapping of each sub-layout to its corresponding alignment on the grid.
AZStd::unordered_map<Qt::Alignment, QBoxLayout*> m_internalLayouts;
};
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,236 @@
/*
* 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 "AzToolsFramework_precompiled.h"
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzToolsFramework/ViewportUi/Button.h>
#include <AzToolsFramework/ViewportUi/Cluster.h>
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
#include <AzToolsFramework/ViewportUi/ViewportUiDisplay.h>
namespace AzToolsFramework::ViewportUi
{
void ViewportUiManager::ConnectViewportUiBus(const int viewportId)
{
ViewportUiRequestBus::Handler::BusConnect(viewportId);
}
void ViewportUiManager::DisconnectViewportUiBus()
{
ViewportUiRequestBus::Handler::BusDisconnect();
}
const ClusterId ViewportUiManager::CreateCluster()
{
auto cluster = AZStd::make_shared<Internal::Cluster>();
m_viewportUi->AddCluster(cluster);
return RegisterNewCluster(cluster);
}
void ViewportUiManager::SetClusterActiveButton(const ClusterId clusterId, const ButtonId buttonId)
{
if (auto clusterEntry = m_clusters.find(clusterId); clusterEntry != m_clusters.end())
{
auto cluster = clusterEntry->second;
cluster->SetHighlightedButton(buttonId);
UpdateClusterUi(cluster.get());
}
}
void ViewportUiManager::RegisterClusterEventHandler(const ClusterId clusterId, AZ::Event<ButtonId>::Handler& handler)
{
if (auto clusterEntry = m_clusters.find(clusterId); clusterEntry != m_clusters.end())
{
auto cluster = clusterEntry->second;
cluster->ConnectEventHandler(handler);
}
}
const ButtonId ViewportUiManager::CreateClusterButton(const ClusterId clusterId, const AZStd::string& icon)
{
if (auto clusterEntry = m_clusters.find(clusterId); clusterEntry != m_clusters.end())
{
auto cluster = clusterEntry->second;
auto newId = cluster->AddButton(icon);
m_viewportUi->AddClusterButton(cluster->GetViewportUiElementId(), cluster->GetButton(newId));
return newId;
}
return ButtonId(0);
}
void ViewportUiManager::RemoveCluster(const ClusterId clusterId)
{
if (auto clusterEntry = m_clusters.find(clusterId); clusterEntry != m_clusters.end())
{
m_clusters.erase(clusterEntry);
m_viewportUi->RemoveViewportUiElement(clusterEntry->second->GetViewportUiElementId());
}
}
static void SetViewportUiElementVisible(
Internal::ViewportUiDisplay* ui, ViewportUiElementId id, bool visible)
{
if (visible)
{
ui->ShowViewportUiElement(id);
}
else
{
ui->HideViewportUiElement(id);
}
}
void ViewportUiManager::SetClusterVisible(ClusterId clusterId, bool visible)
{
if (auto clusterEntry = m_clusters.find(clusterId); clusterEntry != m_clusters.end())
{
auto cluster = clusterEntry->second;
SetViewportUiElementVisible(m_viewportUi.get(), cluster->GetViewportUiElementId(), visible);
}
}
void ViewportUiManager::SetClusterGroupVisible(const AZStd::vector<ClusterId>& clusterGroup, bool visible)
{
for (auto clusterId : clusterGroup)
{
SetClusterVisible(clusterId, visible);
}
}
const TextFieldId ViewportUiManager::CreateTextField(
const AZStd::string& labelText, const AZStd::string& textFieldDefaultText,
TextFieldValidationType validationType)
{
auto textField = AZStd::make_shared<Internal::TextField>(
labelText, textFieldDefaultText, validationType);
m_viewportUi->AddTextField(textField);
return RegisterNewTextField(textField);
}
void ViewportUiManager::SetTextFieldText(TextFieldId textFieldId, const AZStd::string& text)
{
if (auto textFieldEntry = m_textFields.find(textFieldId); textFieldEntry != m_textFields.end())
{
auto textField = textFieldEntry->second;
textField->m_fieldText = text;
UpdateTextFieldUi(textField.get());
}
}
void ViewportUiManager::RegisterTextFieldCallback(
TextFieldId textFieldId, AZ::Event<AZStd::string>::Handler& handler)
{
if (auto textFieldEntry = m_textFields.find(textFieldId); textFieldEntry != m_textFields.end())
{
auto textField = textFieldEntry->second;
textField->ConnectEventHandler(handler);
}
}
void ViewportUiManager::RemoveTextField(TextFieldId textFieldId)
{
if (auto textFieldEntry = m_textFields.find(textFieldId); textFieldEntry != m_textFields.end())
{
m_textFields.erase(textFieldEntry);
m_viewportUi->RemoveViewportUiElement(textFieldEntry->second->m_viewportId);
}
}
void ViewportUiManager::SetTextFieldVisible(TextFieldId textFieldId, bool visible)
{
if (auto textFieldEntry = m_textFields.find(textFieldId); textFieldEntry != m_textFields.end())
{
auto textField = textFieldEntry->second;
SetViewportUiElementVisible(m_viewportUi.get(), textField->m_viewportId, visible);
}
}
void ViewportUiManager::CreateComponentModeBorder(const AZStd::string& borderTitle)
{
m_viewportUi->CreateComponentModeBorder(borderTitle);
}
void ViewportUiManager::RemoveComponentModeBorder()
{
m_viewportUi->RemoveComponentModeBorder();
}
void ViewportUiManager::PressButton(ClusterId clusterId, ButtonId buttonId)
{
// Find cluster using ID and cluster map
if (auto clusterEntry = m_clusters.find(clusterId);
clusterEntry != m_clusters.end())
{
clusterEntry->second->PressButton(buttonId);
}
}
void ViewportUiManager::InitializeViewportUi(QWidget* parent, QWidget* renderOverlay)
{
if (m_viewportUi)
{
AZ_Warning("ViewportUi", false, "Viewport UI already initialized. Removing previous ViewportUiDisplay.");
m_viewportUi.reset();
}
m_viewportUi = AZStd::make_unique<Internal::ViewportUiDisplay>(parent, renderOverlay);
m_viewportUi->InitializeUiOverlay();
}
void ViewportUiManager::Update()
{
m_viewportUi->Update();
for (auto clusterEntry : m_clusters)
{
UpdateClusterUi(clusterEntry.second.get());
}
for (auto textFieldEntry : m_textFields)
{
UpdateTextFieldUi(textFieldEntry.second.get());
}
}
ClusterId ViewportUiManager::RegisterNewCluster(AZStd::shared_ptr<Internal::Cluster>& cluster)
{
ClusterId newId = ClusterId(m_clusters.size() + 1);
cluster->SetClusterId(newId);
m_clusters.insert({ newId, cluster });
return newId;
}
TextFieldId ViewportUiManager::RegisterNewTextField(AZStd::shared_ptr<Internal::TextField>& textField)
{
TextFieldId newId = TextFieldId(m_textFields.size() + 1);
textField->m_textFieldId = newId;
m_textFields.insert({ newId, textField });
return newId;
}
void ViewportUiManager::UpdateClusterUi(Internal::Cluster* cluster)
{
m_viewportUi->UpdateCluster(cluster->GetViewportUiElementId());
}
void ViewportUiManager::UpdateTextFieldUi(Internal::TextField* textField)
{
m_viewportUi->UpdateTextField(textField->m_viewportId);
}
} // namespace AzToolsFramework::ViewportUi
@@ -0,0 +1,77 @@
/*
* 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 <AzToolsFramework/ViewportUi/Button.h>
#include <AzToolsFramework/ViewportUi/Cluster.h>
#include <AzToolsFramework/ViewportUi/TextField.h>
#include <AzToolsFramework/ViewportUi/ViewportUiDisplay.h>
namespace AzToolsFramework::ViewportUi
{
namespace Internal
{
class ViewportUiDisplay;
}
class ViewportUiManager : public ViewportUiRequestBus::Handler
{
public:
ViewportUiManager() = default;
~ViewportUiManager() = default;
// ViewportUiRequestBus ...
const ClusterId CreateCluster() override;
void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) override;
const ButtonId CreateClusterButton(ClusterId clusterId, const AZStd::string& icon) override;
void RegisterClusterEventHandler(ClusterId clusterId, AZ::Event<ButtonId>::Handler& handler) override;
void RemoveCluster(ClusterId clusterId) override;
void SetClusterVisible(ClusterId clusterId, bool visible);
void SetClusterGroupVisible(const AZStd::vector<ClusterId>& clusterGroup, bool visible) override;
const TextFieldId CreateTextField(
const AZStd::string& labelText, const AZStd::string& textFieldDefaultText,
TextFieldValidationType validationType) override;
void SetTextFieldText(TextFieldId textFieldId, const AZStd::string& text) override;
void RegisterTextFieldCallback(
TextFieldId textFieldId, AZ::Event<AZStd::string>::Handler& handler) override;
void RemoveTextField(TextFieldId textFieldId) override;
void SetTextFieldVisible(TextFieldId textFieldId, bool visible) override;
void CreateComponentModeBorder(const AZStd::string& borderTitle) override;
void RemoveComponentModeBorder() override;
void PressButton(ClusterId clusterId, ButtonId buttonId) override;
//! Connects to the correct viewportId bus address.
void ConnectViewportUiBus(const int viewportId);
//! Disconnects from the viewport request bus.
void DisconnectViewportUiBus();
//! Initializes the Viewport UI by attaching it to the given parent and render overlay.
void InitializeViewportUi(QWidget* parent, QWidget* renderOverlay);
//! Updates all registered elements to display up to date.
void Update();
protected:
AZStd::unordered_map<ClusterId, AZStd::shared_ptr<Internal::Cluster>> m_clusters; //!< A map of all registered clusters.
AZStd::unordered_map<TextFieldId, AZStd::shared_ptr<Internal::TextField>> m_textFields; //!< A map of all registered textFields.
AZStd::unique_ptr<Internal::ViewportUiDisplay> m_viewportUi; //!< The lower level graphical API for Viewport UI.
private:
//! Register a new cluster and return its id.
ClusterId RegisterNewCluster(AZStd::shared_ptr<Internal::Cluster>& cluster);
//! Register a new text field and return its id.
TextFieldId RegisterNewTextField(AZStd::shared_ptr<Internal::TextField>& textField);
//! Update the corresponding ui element for the given cluster.
void UpdateClusterUi(Internal::Cluster* cluster);
//! Update the corresponding ui element for the given text field.
void UpdateTextFieldUi(Internal::TextField* textField);
};
} // namespace AzToolsFramework::ViewportUi
@@ -0,0 +1,94 @@
/*
* 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 <AzCore/Component/EntityId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/EBus/Event.h>
#include <AzToolsFramework/Picking/BoundInterface.h>
namespace AzToolsFramework::ViewportUi
{
//! Used to track individual widgets from the Viewport UI.
using ViewportUiElementId = IdType<struct ViewportUiIdType>;
using ButtonId = IdType<struct ButtonIdType>;
using ClusterId = IdType<struct ClusterIdType>;
using TextFieldId = IdType<struct TextFieldIdType>;
inline const ViewportUiElementId InvalidViewportUiElementId = ViewportUiElementId(0);
inline const ButtonId InvalidButtonId = ButtonId(0);
inline const ClusterId InvalidClusterId = ClusterId(0);
inline const int DefaultViewportId = 0;
//! Used to specify the desired validation type for the text field widget.
enum class TextFieldValidationType
{
Int,
Double,
String
};
//! Viewport requests to interact with the Viewport UI. Viewport UI refers to the entire UI overlay (one per viewport).
//! Each widget on the Viewport UI is referred to as an element.
class ViewportUiRequests
{
public:
//! Creates and registers a cluster with the Viewport UI system.
virtual const ClusterId CreateCluster() = 0;
//! Sets the active button of the cluster. This is the button which will display as highlighted.
virtual void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) = 0;
//! Registers a new button onto a cluster.
virtual const ButtonId CreateClusterButton(const ClusterId clusterId, const AZStd::string& icon) = 0;
//! Registers an event handler to handle events from the cluster.
virtual void RegisterClusterEventHandler(ClusterId clusterId, AZ::Event<ButtonId>::Handler& handler) = 0;
//! Removes a cluster from the Viewport UI system.
virtual void RemoveCluster(ClusterId clusterId) = 0;
//! Sets the visibility of the cluster.
virtual void SetClusterVisible(ClusterId clusterId, bool visible) = 0;
//! Sets the visibility of multiple clusters.
virtual void SetClusterGroupVisible(const AZStd::vector<ClusterId>& clusterGroup, bool visible) = 0;
//! Creates and registers a text field with the Viewport UI system.
virtual const TextFieldId CreateTextField(
const AZStd::string& labelText, const AZStd::string& textFieldDefaultText,
TextFieldValidationType validationType) = 0;
//! Set the text that will go inside the text field.
virtual void SetTextFieldText(TextFieldId textFieldId, const AZStd::string& text) = 0;
//! Register an event handler to handle when the text field text changes.
virtual void RegisterTextFieldCallback(
TextFieldId textFieldId, AZ::Event<AZStd::string>::Handler& handler) = 0;
//! Removes a text field from the Viewport UI system.
virtual void RemoveTextField(TextFieldId textFieldId) = 0;
//! Sets the visibility of the text field.
virtual void SetTextFieldVisible(TextFieldId textFieldId, bool visible) = 0;
//! Create the highlight border for Component Mode.
virtual void CreateComponentModeBorder(const AZStd::string& borderTitle) = 0;
//! Remove the highlight border for Component Mode.
virtual void RemoveComponentModeBorder() = 0;
//! Invoke a button press in a cluster.
virtual void PressButton(ClusterId clusterId, ButtonId buttonId) = 0;
};
/// The EBusTraits for ViewportInteractionRequests.
class ViewportUiBusTraits
: public AZ::EBusTraits
{
public:
using BusIdType = int; ///< ViewportId - used to address requests to this EBus.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
};
/// Type to inherit to implement ViewportUiRequests
using ViewportUiRequestBus = AZ::EBus<ViewportUiRequests, ViewportUiBusTraits>;
} // namespace AzToolsFramework::ViewportUi
@@ -0,0 +1,76 @@
/*
* 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 "AzToolsFramework_precompiled.h"
#include "ViewportUiTextField.h"
#include <AzCore/Console/IConsole.h>
#include <QDoubleValidator>
#include <QIntValidator>
namespace AzToolsFramework::ViewportUi::Internal
{
AZ_CVAR(
int, ViewportUiTextFieldLength, 35, nullptr, AZ::ConsoleFunctorFlags::Null,
"The pixel length of the text field part of a ViewportUiTextField");
ViewportUiTextField::ViewportUiTextField(AZStd::shared_ptr<TextField> textField)
: m_label(this)
, m_lineEdit(this)
, m_textField(textField)
{
setContentsMargins(0, 0, 0, 0);
m_label.setText(textField->m_labelText.c_str());
m_lineEdit.setText(textField->m_fieldText.c_str());
// set the layout for the widget and settings such as alignment and margins
auto layout = new QHBoxLayout(this);
layout->setAlignment(Qt::AlignLeft);
layout->addWidget(&m_label);
layout->addWidget(&m_lineEdit);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSizeConstraint(QLayout::SetMaximumSize);
// choose m_validator based on the validationType
switch (textField->m_validationType)
{
case TextFieldValidationType::Int:
m_validator = new QIntValidator(&m_lineEdit);
break;
case TextFieldValidationType::Double:
m_validator = new QDoubleValidator(&m_lineEdit);
break;
case TextFieldValidationType::String:
// nullptr is ok to pass into setValidator
m_validator = nullptr;
break;
default:
m_validator = nullptr;
}
m_lineEdit.setValidator(m_validator);
connect(&m_lineEdit, &QLineEdit::textEdited, &m_lineEdit, [textField](QString text) {
// convert the text using toLocal8Bit().data() as recommended by Qt, then emit signal
textField->m_fieldText = text.toLocal8Bit().data();
textField->m_textEditedEvent.Signal(textField->m_fieldText);
});
}
void ViewportUiTextField::Update()
{
resize(minimumSizeHint());
m_lineEdit.setFixedWidth(ViewportUiTextFieldLength);
}
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,44 @@
/*
* 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 <AzToolsFramework/ViewportUi/TextField.h>
#include <QLabel>
#include <QLineEdit>
#include <QWidget>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <QHBoxLayout>
#include <QVBoxLayout>
AZ_POP_DISABLE_WARNING
namespace AzToolsFramework::ViewportUi::Internal
{
//! Helper class for a widget that holds and manages multiple LabelTextFields.
class ViewportUiTextField
: public QWidget
{
Q_OBJECT
public:
explicit ViewportUiTextField(AZStd::shared_ptr<TextField> textField);
~ViewportUiTextField() = default;
void Update();
private:
QLabel m_label; //<! The text label.
QLineEdit m_lineEdit; //<! The editable text field.
QValidator* m_validator; //<! The validator for the line edit text.
AZStd::shared_ptr<TextField> m_textField; //<! Reference to the text field data struct.
};
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,73 @@
/*
* 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 "AzToolsFramework_precompiled.h"
#include "ViewportUiWidgetCallbacks.h"
namespace AzToolsFramework::ViewportUi::Internal
{
void ViewportUiWidgetCallbacks::AddWidget(
QPointer<QObject> widget, const AZStd::function<void(QPointer<QObject>)>& updateCallback)
{
if (widget.isNull())
{
return;
}
// register and add the widget
m_widgets.push_back(widget);
// register the update callback if provided
if (updateCallback)
{
m_updateCallbacks.insert({ widget, updateCallback });
}
}
void ViewportUiWidgetCallbacks::RemoveWidget(QPointer<QObject> widget)
{
// deregister and remove the widget
m_widgets.erase(AZStd::find(m_widgets.begin(), m_widgets.end(), widget));
}
void ViewportUiWidgetCallbacks::RegisterUpdateCallback(
QPointer<QObject> widget, const AZStd::function<void(QPointer<QObject>)>& callback)
{
// if widget exists on the manager, register the callback
auto callBackWidget = AZStd::find(m_widgets.begin(), m_widgets.end(), widget);
AZ_Assert(callBackWidget != m_widgets.end(), "Unable to register a callback for an unregistered widget.")
if (callBackWidget != m_widgets.end())
{
m_updateCallbacks.insert({ widget, callback });
}
}
void ViewportUiWidgetCallbacks::Update()
{
// iterate through all the callbacks and call them with their respective widgets
for (auto& widget : m_widgets)
{
if (widget.isNull())
{
RemoveWidget(widget);
}
// check if the widget has not been deleted externally
else if (auto callback = m_updateCallbacks.find(widget);
callback != m_updateCallbacks.end())
{
callback->second(widget);
}
}
}
} // namespace AzToolsFramework::ViewportUi::Internal
@@ -0,0 +1,44 @@
/*
* 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 <AzCore/std/function/function_template.h>
#include <AzCore/std/containers/unordered_map.h>
#include <QPointer>
#include <QObject>
#include <QMetaMethod>
namespace AzToolsFramework::ViewportUi::Internal
{
//! Helper class to manage widgets and update them simultaneously.
class ViewportUiWidgetCallbacks
{
public:
ViewportUiWidgetCallbacks() = default;
~ViewportUiWidgetCallbacks() = default;
void AddWidget(QPointer<QObject> widget, const AZStd::function<void(QPointer<QObject>)>& updateCallback = {});
void RemoveWidget(QPointer<QObject> widget);
//! Must call ViewportUiWidgetCallbacks::Update to execute the callback.
void RegisterUpdateCallback(QPointer<QObject> widget, const AZStd::function<void(QPointer<QObject>)>& callback);
void Update();
const AZStd::vector<QPointer<QObject>> GetWidgets() const { return m_widgets; }
protected:
//! A map of all update callbacks and their respective widgets.
//! Note: key is kept as QObject* since QPointer cannot be implicitly hashed.
AZStd::unordered_map<QObject*, AZStd::function<void(QPointer<QObject>)>> m_updateCallbacks;
AZStd::vector<QPointer<QObject>> m_widgets;
};
} // namespace AzToolsFramework::ViewportUi::Internal