1681e97a0c
* ATOM-15369 Expose sorting paramers to DynamicDrawContext's draw functions LYN-3352 LyShine: First element in Hierarchy does not properly draw in UI Editor Add function to DynamicDrawContext to support set sort key for followed draw calls. Use different sort key for background draw and top layer draw in UI canvas.
1308 lines
46 KiB
C++
1308 lines
46 KiB
C++
/*
|
|
* 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 "UiCanvasEditor_precompiled.h"
|
|
|
|
#include "EditorCommon.h"
|
|
|
|
#include "EditorDefs.h"
|
|
#include "Settings.h"
|
|
#include <AzCore/std/containers/map.h>
|
|
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
|
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
|
#include <QViewportSettings.h>
|
|
|
|
#include <LyShine/Bus/UiEditorCanvasBus.h>
|
|
#include <LyShine/Draw2d.h>
|
|
|
|
#include "LyShine.h"
|
|
#include "UiRenderer.h"
|
|
#include "ViewportNudge.h"
|
|
#include "ViewportPivot.h"
|
|
#include "ViewportSnap.h"
|
|
#include "ViewportElement.h"
|
|
#include "RulerWidget.h"
|
|
#include "CanvasHelpers.h"
|
|
#include "AssetDropHelpers.h"
|
|
#include "QtHelpers.h"
|
|
#include <QtGui/private/qhighdpiscaling_p.h>
|
|
|
|
#include <QGridLayout>
|
|
|
|
#include <Atom/RPI.Public/Scene.h>
|
|
#include <Atom/RPI.Public/RenderPipeline.h>
|
|
|
|
#define UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_ELEMENT_BORDERS_KEY "ViewportWidget::m_drawElementBordersFlags"
|
|
#define UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_ELEMENT_BORDERS_DEFAULT ( ViewportWidget::DrawElementBorders_Unselected )
|
|
|
|
#define UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_RULERS_KEY "ViewportWidget::m_rulersVisible"
|
|
#define UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_RULERS_DEFAULT ( false )
|
|
|
|
#define UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_GUIDES_KEY "ViewportWidget::m_guidesVisible"
|
|
#define UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_GUIDES_DEFAULT ( false )
|
|
|
|
namespace
|
|
{
|
|
uint32 GetDrawElementBordersFlags()
|
|
{
|
|
QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
|
|
|
|
settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
|
|
|
|
uint32 result = settings.value(UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_ELEMENT_BORDERS_KEY,
|
|
UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_ELEMENT_BORDERS_DEFAULT).toInt();
|
|
|
|
settings.endGroup();
|
|
|
|
return result;
|
|
}
|
|
|
|
// Persistence.
|
|
void SetDrawElementBordersFlags(uint32 flags)
|
|
{
|
|
QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
|
|
|
|
settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
|
|
|
|
settings.setValue(UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_ELEMENT_BORDERS_KEY,
|
|
flags);
|
|
|
|
settings.endGroup();
|
|
}
|
|
|
|
bool GetPersistentRulerVisibility()
|
|
{
|
|
QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
|
|
|
|
settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
|
|
|
|
bool result = settings.value(UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_RULERS_KEY,
|
|
UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_RULERS_DEFAULT).toBool();
|
|
|
|
settings.endGroup();
|
|
|
|
return result;
|
|
}
|
|
|
|
// Persistence.
|
|
void SetPersistentRulerVisibility(bool rulersVisible)
|
|
{
|
|
QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
|
|
|
|
settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
|
|
|
|
settings.setValue(UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_RULERS_KEY,
|
|
rulersVisible);
|
|
|
|
settings.endGroup();
|
|
}
|
|
|
|
bool GetPersistentGuideVisibility()
|
|
{
|
|
QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
|
|
|
|
settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
|
|
|
|
bool result = settings.value(UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_GUIDES_KEY,
|
|
UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_GUIDES_DEFAULT).toBool();
|
|
|
|
settings.endGroup();
|
|
|
|
return result;
|
|
}
|
|
|
|
// Persistence.
|
|
void SetPersistentGuideVisibility(bool guidesVisible)
|
|
{
|
|
QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
|
|
|
|
settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
|
|
|
|
settings.setValue(UICANVASEDITOR_SETTINGS_VIEWPORTWIDGET_DRAW_GUIDES_KEY,
|
|
guidesVisible);
|
|
|
|
settings.endGroup();
|
|
}
|
|
|
|
// Map Qt event key codes to the game input system keyboard codes
|
|
const AzFramework::InputChannelId* MapQtKeyToAzInputChannelId(int qtKey)
|
|
{
|
|
// The UI runtime only cares about a few special keys
|
|
switch (qtKey)
|
|
{
|
|
case Qt::Key_Tab: return &AzFramework::InputDeviceKeyboard::Key::EditTab;
|
|
case Qt::Key_Backspace: return &AzFramework::InputDeviceKeyboard::Key::EditBackspace;
|
|
case Qt::Key_Return: return &AzFramework::InputDeviceKeyboard::Key::EditEnter;
|
|
case Qt::Key_Enter: return &AzFramework::InputDeviceKeyboard::Key::EditEnter;
|
|
case Qt::Key_Delete: return &AzFramework::InputDeviceKeyboard::Key::NavigationDelete;
|
|
case Qt::Key_Left: return &AzFramework::InputDeviceKeyboard::Key::NavigationArrowLeft;
|
|
case Qt::Key_Up: return &AzFramework::InputDeviceKeyboard::Key::NavigationArrowUp;
|
|
case Qt::Key_Right: return &AzFramework::InputDeviceKeyboard::Key::NavigationArrowRight;
|
|
case Qt::Key_Down: return &AzFramework::InputDeviceKeyboard::Key::NavigationArrowDown;
|
|
case Qt::Key_Home: return &AzFramework::InputDeviceKeyboard::Key::NavigationHome;
|
|
case Qt::Key_End: return &AzFramework::InputDeviceKeyboard::Key::NavigationEnd;
|
|
default: return nullptr;
|
|
}
|
|
}
|
|
|
|
// Map Qt event modifiers to the AzFramework input system modifiers
|
|
AzFramework::ModifierKeyMask MapQtModifiersToAzInputModifierKeys(Qt::KeyboardModifiers qtMods)
|
|
{
|
|
int modifiers = static_cast<int>(AzFramework::ModifierKeyMask::None);
|
|
|
|
if (qtMods & Qt::ShiftModifier)
|
|
{
|
|
modifiers |= static_cast<int>(AzFramework::ModifierKeyMask::ShiftAny);
|
|
}
|
|
|
|
if (qtMods & Qt::ControlModifier)
|
|
{
|
|
modifiers |= static_cast<int>(AzFramework::ModifierKeyMask::CtrlAny);
|
|
}
|
|
|
|
if (qtMods & Qt::AltModifier)
|
|
{
|
|
modifiers |= static_cast<int>(AzFramework::ModifierKeyMask::AltAny);
|
|
}
|
|
|
|
return static_cast<AzFramework::ModifierKeyMask>(modifiers);
|
|
}
|
|
|
|
bool HandleCanvasInputEvent(AZ::EntityId canvasEntityId,
|
|
const AzFramework::InputChannel::Snapshot& inputSnapshot,
|
|
const AZ::Vector2* viewportPos = nullptr,
|
|
const AzFramework::ModifierKeyMask activeModifierKeys = AzFramework::ModifierKeyMask::None)
|
|
{
|
|
bool handled = false;
|
|
EBUS_EVENT_ID_RESULT(handled, canvasEntityId, UiCanvasBus, HandleInputEvent, inputSnapshot, viewportPos, activeModifierKeys);
|
|
|
|
// Execute events that have been queued during the input event handler
|
|
gEnv->pLyShine->ExecuteQueuedEvents();
|
|
|
|
return handled;
|
|
}
|
|
|
|
bool HandleCanvasTextEvent(AZ::EntityId canvasEntityId, const AZStd::string& textUTF8)
|
|
{
|
|
bool handled = false;
|
|
EBUS_EVENT_ID_RESULT(handled, canvasEntityId, UiCanvasBus, HandleTextEvent, textUTF8);
|
|
|
|
// Execute events that have been queued during the input event handler
|
|
gEnv->pLyShine->ExecuteQueuedEvents();
|
|
|
|
return handled;
|
|
}
|
|
|
|
} // anonymous namespace.
|
|
|
|
ViewportWidget::ViewportWidget(EditorWindow* parent)
|
|
: AtomToolsFramework::RenderViewportWidget(parent)
|
|
, m_editorWindow(parent)
|
|
, m_viewportInteraction(new ViewportInteraction(m_editorWindow))
|
|
, m_viewportAnchor(new ViewportAnchor())
|
|
, m_viewportHighlight(new ViewportHighlight())
|
|
, m_viewportBackground(new ViewportCanvasBackground())
|
|
, m_viewportPivot(new ViewportPivot())
|
|
, m_drawElementBordersFlags(GetDrawElementBordersFlags())
|
|
, m_refreshRequested(true)
|
|
, m_canvasRenderIsEnabled(true)
|
|
, m_updateTimer(this)
|
|
, m_previewCanvasScale(1.0f)
|
|
, m_rulersVisible(GetPersistentRulerVisibility())
|
|
, m_guidesVisible(GetPersistentGuideVisibility())
|
|
{
|
|
setAcceptDrops(true);
|
|
|
|
UpdateViewportBackground();
|
|
|
|
InitUiRenderer();
|
|
|
|
SetupShortcuts();
|
|
|
|
// Setup a timer for the maximum refresh rate we want.
|
|
// Refresh is actually triggered by interaction events and by the IdleUpdate. This avoids the UI
|
|
// Editor slowing down the main editor when no UI interaction is occurring.
|
|
QObject::connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(RefreshTick()));
|
|
const int kUpdateIntervalInMillseconds = 1000 / 60; // 60 Hz
|
|
m_updateTimer.start(kUpdateIntervalInMillseconds);
|
|
|
|
// listen to the editor window for changes in mode. When in preview mode hide the rulers.
|
|
QObject::connect(parent,
|
|
&EditorWindow::EditorModeChanged,
|
|
[this](UiEditorMode mode)
|
|
{
|
|
if (mode == UiEditorMode::Preview)
|
|
{
|
|
m_rulersVisible = false;
|
|
}
|
|
else
|
|
{
|
|
m_rulersVisible = GetPersistentRulerVisibility();
|
|
}
|
|
|
|
ApplyRulerVisibility();
|
|
});
|
|
|
|
FontNotificationBus::Handler::BusConnect();
|
|
AZ::TickBus::Handler::BusConnect();
|
|
}
|
|
|
|
ViewportWidget::~ViewportWidget()
|
|
{
|
|
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
|
|
FontNotificationBus::Handler::BusDisconnect();
|
|
AZ::TickBus::Handler::BusDisconnect();
|
|
|
|
m_uiRenderer.reset();
|
|
|
|
// Notify LyShine that this is no longer a valid UiRenderer.
|
|
// Only one viewport/renderer is currently supported in the UI Editor
|
|
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
|
|
lyShine->SetUiRendererForEditor(nullptr);
|
|
}
|
|
|
|
void ViewportWidget::InitUiRenderer()
|
|
{
|
|
m_uiRenderer = AZStd::make_shared<UiRenderer>(GetViewportContext());
|
|
|
|
// Notify LyShine that this is the UiRenderer to be used for rendering
|
|
// UI canvases that are loaded in the UI Editor.
|
|
// Only one viewport/renderer is currently supported in the UI Editor
|
|
CLyShine* lyShine = static_cast<CLyShine*>(gEnv->pLyShine);
|
|
lyShine->SetUiRendererForEditor(m_uiRenderer);
|
|
|
|
m_draw2d = AZStd::make_shared<CDraw2d>(GetViewportContext());
|
|
}
|
|
|
|
ViewportInteraction* ViewportWidget::GetViewportInteraction()
|
|
{
|
|
return m_viewportInteraction.get();
|
|
}
|
|
|
|
bool ViewportWidget::IsDrawingElementBorders(uint32 flags) const
|
|
{
|
|
return (m_drawElementBordersFlags & flags) ? true : false;
|
|
}
|
|
|
|
void ViewportWidget::ToggleDrawElementBorders(uint32 flags)
|
|
{
|
|
m_drawElementBordersFlags ^= flags;
|
|
|
|
// Persistence.
|
|
SetDrawElementBordersFlags(m_drawElementBordersFlags);
|
|
}
|
|
|
|
void ViewportWidget::UpdateViewportBackground()
|
|
{
|
|
const QColor backgroundColor(ViewportHelpers::backgroundColorDark.GetR8(),
|
|
ViewportHelpers::backgroundColorDark.GetG8(),
|
|
ViewportHelpers::backgroundColorDark.GetB8(),
|
|
ViewportHelpers::backgroundColorDark.GetA8());
|
|
|
|
QPalette pal(palette());
|
|
pal.setColor(QPalette::Window, backgroundColor);
|
|
setPalette(pal);
|
|
setAutoFillBackground(true);
|
|
}
|
|
|
|
void ViewportWidget::ActiveCanvasChanged()
|
|
{
|
|
bool canvasLoaded = m_editorWindow->GetCanvas().IsValid();
|
|
if (canvasLoaded)
|
|
{
|
|
m_viewportInteraction->CenterCanvasInViewport();
|
|
}
|
|
|
|
m_viewportInteraction->InitializeToolbars();
|
|
|
|
EntityContextChanged();
|
|
}
|
|
|
|
void ViewportWidget::EntityContextChanged()
|
|
{
|
|
if (m_inObjectPickMode)
|
|
{
|
|
OnEntityPickModeStopped();
|
|
}
|
|
|
|
// Disconnect from the PickModeRequests bus and reconnect with the new entity context
|
|
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
|
|
UiEditorEntityContext* context = m_editorWindow->GetEntityContext();
|
|
if (context)
|
|
{
|
|
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusConnect(context->GetContextId());
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::Refresh()
|
|
{
|
|
m_refreshRequested = true;
|
|
}
|
|
|
|
void ViewportWidget::ClearUntilSafeToRedraw()
|
|
{
|
|
// set flag so that Update will just clear the screen rather than rendering canvas
|
|
m_canvasRenderIsEnabled = false;
|
|
|
|
#ifdef LYSHINE_ATOM_TODO // check if still needed
|
|
// Force an update
|
|
Update();
|
|
#endif
|
|
|
|
// Schedule a timer to set the m_canvasRenderIsEnabled flag
|
|
// using a time of zero just waits until there is nothing on the event queue
|
|
QTimer::singleShot(0, this, SLOT(EnableCanvasRender()));
|
|
}
|
|
|
|
void ViewportWidget::SetRedrawEnabled(bool enabled)
|
|
{
|
|
m_canvasRenderIsEnabled = enabled;
|
|
}
|
|
|
|
void ViewportWidget::PickItem(AZ::EntityId entityId)
|
|
{
|
|
AzToolsFramework::EditorPickModeRequestBus::Broadcast(
|
|
&AzToolsFramework::EditorPickModeRequests::PickModeSelectEntity, entityId);
|
|
|
|
AzToolsFramework::EditorPickModeRequestBus::Broadcast(
|
|
&AzToolsFramework::EditorPickModeRequests::StopEntityPickMode);
|
|
}
|
|
|
|
QWidget* ViewportWidget::CreateViewportWithRulersWidget(QWidget* parent)
|
|
{
|
|
QWidget* viewportWithRulersWidget = new QWidget(parent);
|
|
|
|
QGridLayout* viewportWithRulersLayout = new QGridLayout(viewportWithRulersWidget);
|
|
viewportWithRulersLayout->setContentsMargins(0, 0, 0, 0);
|
|
viewportWithRulersLayout->setSpacing(0);
|
|
|
|
m_rulerHorizontal = new RulerWidget(RulerWidget::Orientation::Horizontal, viewportWithRulersWidget, m_editorWindow);
|
|
m_rulerVertical = new RulerWidget(RulerWidget::Orientation::Vertical, viewportWithRulersWidget, m_editorWindow);
|
|
|
|
m_rulerCorner = new QWidget();
|
|
m_rulerCorner->setBackgroundRole(QPalette::Window);
|
|
|
|
viewportWithRulersLayout->addWidget(m_rulerCorner,0,0);
|
|
viewportWithRulersLayout->addWidget(m_rulerHorizontal,0,1);
|
|
viewportWithRulersLayout->addWidget(m_rulerVertical,1,0);
|
|
viewportWithRulersLayout->addWidget(this,1,1);
|
|
|
|
ApplyRulerVisibility();
|
|
|
|
return viewportWithRulersWidget;
|
|
}
|
|
|
|
void ViewportWidget::ShowRulers(bool show)
|
|
{
|
|
if (show != m_rulersVisible)
|
|
{
|
|
m_rulersVisible = show;
|
|
ApplyRulerVisibility();
|
|
SetPersistentRulerVisibility(m_rulersVisible);
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::RefreshRulers()
|
|
{
|
|
if (m_rulersVisible)
|
|
{
|
|
m_rulerHorizontal->update();
|
|
m_rulerVertical->update();
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::SetRulerCursorPositions(const QPoint& globalPos)
|
|
{
|
|
if (m_rulersVisible)
|
|
{
|
|
m_rulerHorizontal->SetCursorPos(globalPos);
|
|
m_rulerVertical->SetCursorPos(globalPos);
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::ShowGuides(bool show)
|
|
{
|
|
if (show != m_guidesVisible)
|
|
{
|
|
m_guidesVisible = show;
|
|
SetPersistentGuideVisibility(m_guidesVisible);
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::contextMenuEvent(QContextMenuEvent* e)
|
|
{
|
|
if (m_editorWindow->GetCanvas().IsValid())
|
|
{
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
// The context menu.
|
|
const QPoint pos = e->pos();
|
|
HierarchyMenu contextMenu(m_editorWindow->GetHierarchy(),
|
|
HierarchyMenu::Show::kCutCopyPaste |
|
|
HierarchyMenu::Show::kSavePrefab |
|
|
HierarchyMenu::Show::kNew_EmptyElement |
|
|
HierarchyMenu::Show::kNew_ElementFromPrefabs |
|
|
HierarchyMenu::Show::kDeleteElement |
|
|
HierarchyMenu::Show::kNewSlice |
|
|
HierarchyMenu::Show::kNew_InstantiateSlice |
|
|
HierarchyMenu::Show::kPushToSlice |
|
|
HierarchyMenu::Show::kEditorOnly |
|
|
HierarchyMenu::Show::kFindElements,
|
|
true,
|
|
&pos);
|
|
|
|
contextMenu.exec(e->globalPos());
|
|
}
|
|
}
|
|
|
|
RenderViewportWidget::contextMenuEvent(e);
|
|
}
|
|
|
|
#ifdef LYSHINE_ATOM_TODO // check if still needed
|
|
void ViewportWidget::HandleSignalRender([[maybe_unused]] const SRenderContext& context)
|
|
{
|
|
// Called from QViewport when redrawing the viewport.
|
|
// Triggered from a QViewport resize event or from our call to QViewport::Update
|
|
if (m_canvasRenderIsEnabled)
|
|
{
|
|
gEnv->pRenderer->SetSrgbWrite(true);
|
|
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
RenderEditMode();
|
|
}
|
|
else // if (editorMode == UiEditorMode::Preview)
|
|
{
|
|
RenderPreviewMode();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void ViewportWidget::UserSelectionChanged(HierarchyItemRawPtrList* items)
|
|
{
|
|
Refresh();
|
|
|
|
if (!items)
|
|
{
|
|
m_viewportInteraction->ClearInteraction();
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::EnableCanvasRender()
|
|
{
|
|
m_canvasRenderIsEnabled = true;
|
|
|
|
// force a redraw
|
|
Refresh();
|
|
RefreshTick();
|
|
}
|
|
|
|
void ViewportWidget::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
|
{
|
|
if (!m_uiRenderer->IsReady() || !m_canvasRenderIsEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifdef LYSHINE_ATOM_TODO
|
|
gEnv->pRenderer->SetSrgbWrite(true);
|
|
#endif
|
|
|
|
// Set up to render a frame to this viewport's window
|
|
GetViewportContext()->RenderTick();
|
|
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
RenderEditMode(deltaTime);
|
|
}
|
|
else // if (editorMode == UiEditorMode::Preview)
|
|
{
|
|
RenderPreviewMode(deltaTime);
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::RefreshTick()
|
|
{
|
|
#ifdef LYSHINE_EDITOR_TODO // still need this?
|
|
if (m_refreshRequested)
|
|
{
|
|
if (m_canvasRenderIsEnabled)
|
|
{
|
|
// Redraw the canvas
|
|
Update();
|
|
}
|
|
m_refreshRequested = false;
|
|
|
|
// in case we were called manually, reset the timer
|
|
m_updateTimer.start();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ViewportWidget::mousePressEvent(QMouseEvent* ev)
|
|
{
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
QMouseEvent scaledEvent(ev->type(), WidgetToViewport(ev->localPos()), ev->button(), ev->buttons(), ev->modifiers());
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
// in Edit mode just send input to ViewportInteraction
|
|
m_viewportInteraction->MousePressEvent(&scaledEvent);
|
|
}
|
|
else // if (editorMode == UiEditorMode::Preview)
|
|
{
|
|
// In Preview mode convert the event into a game input event and send to canvas
|
|
AZ::EntityId canvasEntityId = m_editorWindow->GetPreviewModeCanvas();
|
|
if (canvasEntityId.IsValid())
|
|
{
|
|
if (ev->button() == Qt::LeftButton)
|
|
{
|
|
// Send event to this canvas
|
|
QPointF scaledPos = WidgetToViewport(ev->localPos());
|
|
const AZ::Vector2 viewportPosition(aznumeric_cast<float>(scaledPos.x()), aznumeric_cast<float>(scaledPos.y()));
|
|
const AzFramework::InputChannel::Snapshot inputSnapshot(AzFramework::InputDeviceMouse::Button::Left,
|
|
AzFramework::InputDeviceMouse::Id,
|
|
AzFramework::InputChannel::State::Began);
|
|
HandleCanvasInputEvent(canvasEntityId, inputSnapshot, &viewportPosition);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note: do not propagate this event to parent QViewport, otherwise
|
|
// it will manipulate the mouse position in unexpected ways.
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void ViewportWidget::mouseMoveEvent(QMouseEvent* ev)
|
|
{
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
QMouseEvent scaledEvent(ev->type(), WidgetToViewport(ev->localPos()), ev->button(), ev->buttons(), ev->modifiers());
|
|
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
// in Edit mode just send input to ViewportInteraction
|
|
m_viewportInteraction->MouseMoveEvent(&scaledEvent,
|
|
m_editorWindow->GetHierarchy()->selectedItems());
|
|
|
|
SetRulerCursorPositions(ev->globalPos());
|
|
}
|
|
else // if (editorMode == UiEditorMode::Preview)
|
|
{
|
|
// In Preview mode convert the event into a game input event and send to canvas
|
|
AZ::EntityId canvasEntityId = m_editorWindow->GetPreviewModeCanvas();
|
|
if (canvasEntityId.IsValid())
|
|
{
|
|
QPointF scaledPos = WidgetToViewport(ev->localPos());
|
|
const AZ::Vector2 viewportPosition(aznumeric_cast<float>(scaledPos.x()), aznumeric_cast<float>(scaledPos.y()));
|
|
const AzFramework::InputChannelId& channelId = (ev->buttons() & Qt::LeftButton) ?
|
|
AzFramework::InputDeviceMouse::Button::Left :
|
|
AzFramework::InputDeviceMouse::SystemCursorPosition;
|
|
const AzFramework::InputChannel::Snapshot inputSnapshot(channelId,
|
|
AzFramework::InputDeviceMouse::Id,
|
|
AzFramework::InputChannel::State::Updated);
|
|
HandleCanvasInputEvent(canvasEntityId, inputSnapshot, &viewportPosition);
|
|
}
|
|
}
|
|
|
|
// Note: do not propagate this event to parent QViewport, otherwise
|
|
// it will manipulate the mouse position in unexpected ways.
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void ViewportWidget::mouseReleaseEvent(QMouseEvent* ev)
|
|
{
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
QMouseEvent scaledEvent(ev->type(), WidgetToViewport(ev->localPos()), ev->button(), ev->buttons(), ev->modifiers());
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
// in Edit mode just send input to ViewportInteraction
|
|
m_viewportInteraction->MouseReleaseEvent(&scaledEvent,
|
|
m_editorWindow->GetHierarchy()->selectedItems());
|
|
}
|
|
else // if (editorMode == UiEditorMode::Preview)
|
|
{
|
|
// In Preview mode convert the event into a game input event and send to canvas
|
|
AZ::EntityId canvasEntityId = m_editorWindow->GetPreviewModeCanvas();
|
|
if (canvasEntityId.IsValid())
|
|
{
|
|
if (ev->button() == Qt::LeftButton)
|
|
{
|
|
// Send event to this canvas
|
|
QPointF scaledPos = WidgetToViewport(ev->localPos());
|
|
const AZ::Vector2 viewportPosition(aznumeric_cast<float>(scaledPos.x()), aznumeric_cast<float>(scaledPos.y()));
|
|
const AzFramework::InputChannel::Snapshot inputSnapshot(AzFramework::InputDeviceMouse::Button::Left,
|
|
AzFramework::InputDeviceMouse::Id,
|
|
AzFramework::InputChannel::State::Ended);
|
|
HandleCanvasInputEvent(canvasEntityId, inputSnapshot, &viewportPosition);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note: do not propagate this event to parent QViewport, otherwise
|
|
// it will manipulate the mouse position in unexpected ways.
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void ViewportWidget::wheelEvent(QWheelEvent* ev)
|
|
{
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
QWheelEvent scaledEvent(
|
|
WidgetToViewport(ev->position()),
|
|
ev->globalPosition(),
|
|
ev->pixelDelta(),
|
|
ev->angleDelta(),
|
|
ev->buttons(),
|
|
ev->modifiers(),
|
|
ev->phase(),
|
|
ev->inverted()
|
|
);
|
|
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
// in Edit mode just send input to ViewportInteraction
|
|
m_viewportInteraction->MouseWheelEvent(&scaledEvent);
|
|
}
|
|
|
|
RenderViewportWidget::wheelEvent(ev);
|
|
|
|
Refresh();
|
|
}
|
|
|
|
bool ViewportWidget::event(QEvent* ev)
|
|
{
|
|
if (ev->type() == QEvent::ShortcutOverride)
|
|
{
|
|
// When a shortcut is matched, Qt's event processing sends out a shortcut override event
|
|
// to allow other systems to override it. If it's not overridden, then the key events
|
|
// get processed as a shortcut, even if the widget that's the target has a keyPress event
|
|
// handler. In our case this causes a problem in preview mode for the Key_Delete event.
|
|
// So, if we are preview mode avoid treating Key_Delete as a shortcut.
|
|
|
|
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(ev);
|
|
int key = keyEvent->key();
|
|
|
|
// Override the space bar shortcut so that the key gets handled by the viewport's KeyPress/KeyRelease
|
|
// events when the viewport has the focus. The space bar is set up as a shortcut in order to give the
|
|
// viewport the focus and activate the space bar when another widget has the focus. Once the shortcut
|
|
// is pressed and focus is given to the viewport, the viewport takes over handling the space bar via
|
|
// the KeyPress/KeyRelease events
|
|
if (key == Qt::Key_Space)
|
|
{
|
|
ev->accept();
|
|
return true;
|
|
}
|
|
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
if (editorMode == UiEditorMode::Preview)
|
|
{
|
|
switch (key)
|
|
{
|
|
case Qt::Key_Delete:
|
|
// Ignore nudge shortcuts in preview mode so that the KeyPressEvent will be sent
|
|
case Qt::Key_Up:
|
|
case Qt::Key_Down:
|
|
case Qt::Key_Left:
|
|
case Qt::Key_Right:
|
|
{
|
|
ev->accept();
|
|
return true;
|
|
}
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
bool result = QWidget::event(ev);
|
|
return result;
|
|
}
|
|
|
|
void ViewportWidget::keyPressEvent(QKeyEvent* event)
|
|
{
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
// in Edit mode just send input to ViewportInteraction
|
|
bool handled = m_viewportInteraction->KeyPressEvent(event);
|
|
if (!handled)
|
|
{
|
|
RenderViewportWidget::keyPressEvent(event);
|
|
}
|
|
}
|
|
else // if (editorMode == UiEditorMode::Preview)
|
|
{
|
|
// In Preview mode convert the event into a game input event and send to canvas
|
|
AZ::EntityId canvasEntityId = m_editorWindow->GetPreviewModeCanvas();
|
|
if (canvasEntityId.IsValid())
|
|
{
|
|
// Send event to this canvas
|
|
const AzFramework::InputChannelId* inputChannelId = MapQtKeyToAzInputChannelId(event->key());
|
|
const AzFramework::ModifierKeyMask activeModifierKeys = MapQtModifiersToAzInputModifierKeys(event->modifiers());
|
|
if (inputChannelId)
|
|
{
|
|
const AzFramework::InputChannel::Snapshot inputSnapshot(*inputChannelId,
|
|
AzFramework::InputDeviceKeyboard::Id,
|
|
AzFramework::InputChannel::State::Began);
|
|
HandleCanvasInputEvent(canvasEntityId, inputSnapshot, nullptr, activeModifierKeys);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::focusOutEvent([[maybe_unused]] QFocusEvent* ev)
|
|
{
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
m_viewportInteraction->ClearInteraction();
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::keyReleaseEvent(QKeyEvent* event)
|
|
{
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
// in Edit mode just send input to ViewportInteraction
|
|
bool handled = m_viewportInteraction->KeyReleaseEvent(event);
|
|
if (!handled)
|
|
{
|
|
RenderViewportWidget::keyReleaseEvent(event);
|
|
}
|
|
}
|
|
else if (editorMode == UiEditorMode::Preview)
|
|
{
|
|
AZ::EntityId canvasEntityId = m_editorWindow->GetPreviewModeCanvas();
|
|
if (canvasEntityId.IsValid())
|
|
{
|
|
bool handled = false;
|
|
|
|
// Send event to this canvas
|
|
const AzFramework::InputChannelId* inputChannelId = MapQtKeyToAzInputChannelId(event->key());
|
|
const AzFramework::ModifierKeyMask activeModifierKeys = MapQtModifiersToAzInputModifierKeys(event->modifiers());
|
|
if (inputChannelId)
|
|
{
|
|
const AzFramework::InputChannel::Snapshot inputSnapshot(*inputChannelId,
|
|
AzFramework::InputDeviceKeyboard::Id,
|
|
AzFramework::InputChannel::State::Ended);
|
|
HandleCanvasInputEvent(canvasEntityId, inputSnapshot, nullptr, activeModifierKeys);
|
|
}
|
|
|
|
QString string = event->text();
|
|
if (string.length() != 0 && !handled)
|
|
{
|
|
AZStd::string textUTF8 = string.toUtf8().data();
|
|
HandleCanvasTextEvent(canvasEntityId, textUTF8);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AZ_Assert(0, "Invalid editorMode: %d", editorMode);
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::resizeEvent(QResizeEvent* ev)
|
|
{
|
|
m_editorWindow->GetPreviewToolbar()->ViewportHasResized(ev);
|
|
|
|
if (m_editorWindow->GetCanvas().IsValid())
|
|
{
|
|
UiEditorMode editorMode = m_editorWindow->GetEditorMode();
|
|
if (editorMode == UiEditorMode::Edit)
|
|
{
|
|
if (m_viewportInteraction->ShouldScaleToFitOnViewportResize())
|
|
{
|
|
m_viewportInteraction->CenterCanvasInViewport();
|
|
}
|
|
}
|
|
}
|
|
|
|
RenderViewportWidget::resizeEvent(ev);
|
|
}
|
|
|
|
bool ViewportWidget::AcceptsMimeData(const QMimeData* mimeData)
|
|
{
|
|
bool canvasLoaded = m_editorWindow->GetCanvas().IsValid();
|
|
if (!canvasLoaded)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return AssetDropHelpers::DoesMimeDataContainSliceOrComponentAssets(mimeData);
|
|
}
|
|
|
|
void ViewportWidget::dragEnterEvent(QDragEnterEvent* event)
|
|
{
|
|
if (AcceptsMimeData(event->mimeData()))
|
|
{
|
|
event->accept();
|
|
}
|
|
else
|
|
{
|
|
event->ignore();
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::dropEvent(QDropEvent* event)
|
|
{
|
|
if (AcceptsMimeData(event->mimeData()))
|
|
{
|
|
const AZ::EntityId targetEntityId;
|
|
const bool onElement = false;
|
|
const int childIndex = -1;
|
|
const QPoint pos = event->pos();
|
|
m_editorWindow->GetHierarchy()->DropMimeDataAssets(event->mimeData(), targetEntityId, onElement, childIndex, &pos);
|
|
event->accept();
|
|
|
|
// Put focus on the viewport widget
|
|
activateWindow();
|
|
setFocus();
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::OnEntityPickModeStarted()
|
|
{
|
|
m_inObjectPickMode = true;
|
|
m_viewportInteraction->StartObjectPickMode();
|
|
}
|
|
|
|
void ViewportWidget::OnEntityPickModeStopped()
|
|
{
|
|
if (m_inObjectPickMode)
|
|
{
|
|
m_inObjectPickMode = false;
|
|
m_viewportInteraction->StopObjectPickMode();
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::OnFontsReloaded()
|
|
{
|
|
m_fontTextureHasChanged = true;
|
|
}
|
|
|
|
void ViewportWidget::OnFontTextureUpdated([[maybe_unused]] IFFont* font)
|
|
{
|
|
m_fontTextureHasChanged = true;
|
|
}
|
|
|
|
QPointF ViewportWidget::WidgetToViewport(const QPointF & point) const
|
|
{
|
|
return point * WidgetToViewportFactor();
|
|
}
|
|
|
|
void ViewportWidget::RenderEditMode(float deltaTime)
|
|
{
|
|
// sort keys for different layers
|
|
static const int64_t backgroundKey = -0x1000;
|
|
static const int64_t topLayerKey = 0x1000000;
|
|
|
|
if (m_fontTextureHasChanged)
|
|
{
|
|
// A font texture has changed since we last rendered. Force a render graph update for each loaded canvas
|
|
m_editorWindow->FontTextureHasChanged();
|
|
m_fontTextureHasChanged = false;
|
|
}
|
|
|
|
AZ::EntityId canvasEntityId = m_editorWindow->GetCanvas();
|
|
if (!canvasEntityId.IsValid())
|
|
{
|
|
return; // this can happen if a render happens during a restart
|
|
}
|
|
|
|
Draw2dHelper draw2d(m_draw2d.get()); // sets and resets 2D draw mode in constructor/destructor
|
|
|
|
QTreeWidgetItemRawPtrQList selection = m_editorWindow->GetHierarchy()->selectedItems();
|
|
|
|
AZ::Vector2 canvasSize;
|
|
EBUS_EVENT_ID_RESULT(canvasSize, canvasEntityId, UiCanvasBus, GetCanvasSize);
|
|
|
|
m_draw2d->SetSortKey(backgroundKey);
|
|
m_viewportBackground->Draw(draw2d,
|
|
canvasSize,
|
|
m_viewportInteraction->GetCanvasToViewportScale(),
|
|
m_viewportInteraction->GetCanvasToViewportTranslation());
|
|
|
|
AZ::Vector2 viewportSize(aznumeric_cast<float>(size().width()), aznumeric_cast<float>(size().height()));
|
|
viewportSize *= QHighDpiScaling::factor(windowHandle()->screen());
|
|
|
|
#ifdef LYSHINE_ATOM_TODO
|
|
// clear the stencil buffer before rendering each canvas - required for masking
|
|
// NOTE: the FRT_CLEAR_IMMEDIATE is required since we will not be setting the render target
|
|
ColorF viewportBackgroundColor(0, 0, 0, 0); // if clearing color we want to set alpha to zero also
|
|
gEnv->pRenderer->ClearTargetsImmediately(FRT_CLEAR_STENCIL, viewportBackgroundColor);
|
|
#endif
|
|
|
|
// Set the target size of the canvas
|
|
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, false, canvasSize);
|
|
|
|
// Update this canvas (must be done after SetTargetCanvasSize)
|
|
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, false);
|
|
|
|
// Render this canvas
|
|
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, RenderCanvasInEditorViewport, false, viewportSize);
|
|
|
|
m_draw2d->SetSortKey(topLayerKey);
|
|
// Draw borders around selected and unselected UI elements in the viewport
|
|
// depending on the flags in m_drawElementBordersFlags
|
|
HierarchyItemRawPtrList selectedItems = SelectionHelpers::GetSelectedHierarchyItems(m_editorWindow->GetHierarchy(), selection);
|
|
m_viewportHighlight->Draw(draw2d,
|
|
m_editorWindow->GetHierarchy()->invisibleRootItem(),
|
|
selectedItems,
|
|
m_drawElementBordersFlags);
|
|
|
|
// Draw primary gizmos and guide lines
|
|
m_viewportInteraction->Draw(draw2d, selection);
|
|
|
|
// Draw any interaction display for the rulers that is in the viewport
|
|
m_rulerHorizontal->DrawForViewport(draw2d);
|
|
m_rulerVertical->DrawForViewport(draw2d);
|
|
|
|
// Draw secondary gizmos
|
|
if (ViewportInteraction::InteractionMode::ROTATE == m_viewportInteraction->GetMode())
|
|
{
|
|
// Draw the pivots and degrees only in Rotate mode
|
|
LyShine::EntityArray selectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selection);
|
|
for (auto element : selectedElements)
|
|
{
|
|
bool isHighlighted = (m_viewportInteraction->GetActiveElement() == element) &&
|
|
(m_viewportInteraction->GetInteractionType() == ViewportInteraction::InteractionType::PIVOT);
|
|
m_viewportPivot->Draw(draw2d, element, isHighlighted);
|
|
|
|
ViewportHelpers::DrawRotationValue(element, m_viewportInteraction.get(), m_viewportPivot.get(), draw2d);
|
|
}
|
|
}
|
|
else if (ViewportInteraction::InteractionMode::MOVE == m_viewportInteraction->GetMode() ||
|
|
ViewportInteraction::InteractionMode::ANCHOR == m_viewportInteraction->GetMode())
|
|
{
|
|
// Draw the anchors only if we're in Anchor or Move mode
|
|
|
|
// We draw extra anchor related data when we are in the middle of an interaction
|
|
bool leftButtonIsActive = m_viewportInteraction->GetLeftButtonIsActive();
|
|
bool spaceBarIsActive = m_viewportInteraction->GetSpaceBarIsActive();
|
|
bool isInteracting = leftButtonIsActive && !spaceBarIsActive &&
|
|
m_viewportInteraction->GetInteractionType() != ViewportInteraction::InteractionType::NONE &&
|
|
m_viewportInteraction->GetInteractionType() != ViewportInteraction::InteractionType::GUIDE;
|
|
|
|
ViewportHelpers::SelectedAnchors highlightedAnchors = m_viewportInteraction->GetGrabbedAnchors();
|
|
|
|
// These flags affect what parts of the anchor display is drawn
|
|
bool drawUnTransformedRect = false;
|
|
bool drawAnchorLines = false;
|
|
bool drawLinesToParent = false;
|
|
|
|
bool anchorInteractionEnabled = m_viewportInteraction->GetMode() == ViewportInteraction::InteractionMode::ANCHOR && selectedItems.size() == 1;
|
|
|
|
if (isInteracting)
|
|
{
|
|
if (m_viewportInteraction->GetMode() == ViewportInteraction::InteractionMode::MOVE)
|
|
{
|
|
// when interacting in move mode (changing offsets) we draw the anchor lines from the anchor to the element
|
|
// and also draw a faint untransformed rect around the element
|
|
drawUnTransformedRect = true;
|
|
drawAnchorLines = true;
|
|
}
|
|
else
|
|
{
|
|
// when interacting in anchor mode we draw lines from the anchor to the parent rect
|
|
drawLinesToParent = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// not interacting but could be hovering over anchors
|
|
if (highlightedAnchors.Any())
|
|
{
|
|
// if the anchors are highlighted (whether actually moving or not) we want to draw distance
|
|
// lines from the anchor to the edges of it's parent rect. In this case we do NOT want to
|
|
// draw the lines from the anchor to this element's rect or pivot
|
|
drawLinesToParent = true;
|
|
}
|
|
}
|
|
|
|
// for all the top level selected elements, draw the anchors
|
|
LyShine::EntityArray selectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selection);
|
|
for (auto element : selectedElements)
|
|
{
|
|
m_viewportAnchor->Draw(draw2d,
|
|
element,
|
|
drawUnTransformedRect,
|
|
drawAnchorLines,
|
|
drawLinesToParent,
|
|
anchorInteractionEnabled,
|
|
highlightedAnchors);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::RenderPreviewMode(float deltaTime)
|
|
{
|
|
AZ::EntityId canvasEntityId = m_editorWindow->GetPreviewModeCanvas();
|
|
|
|
if (m_fontTextureHasChanged)
|
|
{
|
|
// A font texture has changed since we last rendered. Force a render graph update for each loaded canvas
|
|
m_editorWindow->FontTextureHasChanged();
|
|
m_fontTextureHasChanged = false;
|
|
}
|
|
|
|
// Rather than scaling to exactly fit we try to draw at one of these preset scale factors
|
|
// to make it it bit more obvious that the canvas size is changing
|
|
float zoomScales[] = {
|
|
1.00f,
|
|
0.75f,
|
|
0.50f,
|
|
0.25f,
|
|
0.10f,
|
|
0.05f
|
|
};
|
|
|
|
if (canvasEntityId.IsValid())
|
|
{
|
|
// Get the canvas size
|
|
AZ::Vector2 viewportSize(aznumeric_cast<float>(size().width()), aznumeric_cast<float>(size().height()));
|
|
viewportSize *= QHighDpiScaling::factor(windowHandle()->screen());
|
|
|
|
AZ::Vector2 canvasSize = m_editorWindow->GetPreviewCanvasSize();
|
|
if (canvasSize.GetX() == 0.0f && canvasSize.GetY() == 0.0f)
|
|
{
|
|
// special value of (0,0) means use the viewport size
|
|
canvasSize = viewportSize;
|
|
}
|
|
|
|
// work out what scale to use for the canvasToViewport matrix
|
|
float scale = 1.0f;
|
|
if (canvasSize.GetX() > viewportSize.GetX())
|
|
{
|
|
if (canvasSize.GetX() >= 1.0f) // avoid divide by zero
|
|
{
|
|
scale = viewportSize.GetX() / canvasSize.GetX();
|
|
}
|
|
}
|
|
if (canvasSize.GetY() > viewportSize.GetY())
|
|
{
|
|
if (canvasSize.GetY() >= 1.0f) // avoid divide by zero
|
|
{
|
|
float scaleY = viewportSize.GetY() / canvasSize.GetY();
|
|
if (scaleY < scale)
|
|
{
|
|
scale = scaleY;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the target size of the canvas
|
|
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetTargetCanvasSize, true, canvasSize);
|
|
|
|
// Update this canvas (must be done after SetTargetCanvasSize)
|
|
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, UpdateCanvasInEditorViewport, deltaTime, true);
|
|
|
|
// Execute events that have been queued during the canvas update
|
|
gEnv->pLyShine->ExecuteQueuedEvents();
|
|
|
|
// match scale to one of the predefined scales. If the scale is so small
|
|
// that it is less than the smallest scale then leave it as it is
|
|
for (int i = 0; i < AZ_ARRAY_SIZE(zoomScales); ++i)
|
|
{
|
|
if (scale >= zoomScales[i])
|
|
{
|
|
scale = zoomScales[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update the toolbar to show the current scale
|
|
if (scale != m_previewCanvasScale)
|
|
{
|
|
m_previewCanvasScale = scale;
|
|
m_editorWindow->GetPreviewToolbar()->UpdatePreviewCanvasScale(scale);
|
|
}
|
|
|
|
// Set up the canvasToViewportMatrix
|
|
AZ::Vector3 scale3(scale, scale, 1.0f);
|
|
AZ::Vector3 translation((viewportSize.GetX() - (canvasSize.GetX() * scale)) * 0.5f,
|
|
(viewportSize.GetY() - (canvasSize.GetY() * scale)) * 0.5f, 0.0f);
|
|
AZ::Matrix4x4 canvasToViewportMatrix = AZ::Matrix4x4::CreateScale(scale3);
|
|
canvasToViewportMatrix.SetTranslation(translation);
|
|
EBUS_EVENT_ID(canvasEntityId, UiCanvasBus, SetCanvasToViewportMatrix, canvasToViewportMatrix);
|
|
|
|
#ifdef LYSHINE_ATOM_TODO
|
|
// clear the stencil buffer before rendering each canvas - required for masking
|
|
// NOTE: the FRT_CLEAR_IMMEDIATE is required since we will not be setting the render target
|
|
// We also clear the color to a mid grey so that we can see the bounds of the canvas
|
|
ColorF viewportBackgroundColor(0.5f, 0.5f, 0.5f, 0); // if clearing color we want to set alpha to zero also
|
|
gEnv->pRenderer->ClearTargetsImmediately(FRT_CLEAR, viewportBackgroundColor);
|
|
#endif
|
|
|
|
#ifdef LYSHINE_ATOM_TODO
|
|
// Render a black rectangle covering the canvas area. This allows the canvas bounds to be visible when the canvas size is
|
|
// not exactly the same as the viewport size
|
|
AZ::Vector2 topLeftInViewportSpace = CanvasHelpers::GetViewportPoint(canvasEntityId, AZ::Vector2(0.0f, 0.0f));
|
|
AZ::Vector2 bottomRightInViewportSpace = CanvasHelpers::GetViewportPoint(canvasEntityId, canvasSize);
|
|
AZ::Vector2 sizeInViewportSpace = bottomRightInViewportSpace - topLeftInViewportSpace;
|
|
Draw2dHelper draw2d(m_draw2d.get())
|
|
int texId = gEnv->pRenderer->GetBlackTextureId();
|
|
draw2d.DrawImage(texId, topLeftInViewportSpace, sizeInViewportSpace);
|
|
#endif
|
|
|
|
// Render this canvas
|
|
// NOTE: the displayBounds param is always false. If we wanted a debug option to display the bounds
|
|
// in preview mode we would need to render the deferred primitives after this call so that they
|
|
// show up in the correct viewport
|
|
EBUS_EVENT_ID(canvasEntityId, UiEditorCanvasBus, RenderCanvasInEditorViewport, true, viewportSize);
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::SetupShortcuts()
|
|
{
|
|
// Actions with shortcuts are created instead of direct shortcuts because the shortcut dispatcher only looks for matching actions
|
|
|
|
// Create nudge shortcuts that are active across the entire UI Editor window. Any widgets (such as the spin box widget) that
|
|
// handle the same keys and want the shortcut to be ignored need to handle that with a shortcut override event.
|
|
// In preview mode, the nudge shortcuts are ignored via the shortcut override event. KeyPressEvents are sent instead,
|
|
// and passed along to the canvas
|
|
|
|
// Nudge up
|
|
{
|
|
QAction* action = new QAction("Up", this);
|
|
action->setShortcut(QKeySequence(Qt::Key_Up));
|
|
QObject::connect(action,
|
|
&QAction::triggered,
|
|
[this]()
|
|
{
|
|
m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Up, ViewportInteraction::NudgeSpeed::Slow);
|
|
});
|
|
addAction(action);
|
|
}
|
|
|
|
// Nudge up fast
|
|
{
|
|
QAction* action = new QAction("Up Fast", this);
|
|
action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Up));
|
|
QObject::connect(action,
|
|
&QAction::triggered,
|
|
[this]()
|
|
{
|
|
m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Up, ViewportInteraction::NudgeSpeed::Fast);
|
|
});
|
|
addAction(action);
|
|
}
|
|
|
|
// Nudge down
|
|
{
|
|
QAction* action = new QAction("Down", this);
|
|
action->setShortcut(QKeySequence(Qt::Key_Down));
|
|
QObject::connect(action,
|
|
&QAction::triggered,
|
|
[this]()
|
|
{
|
|
m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Down, ViewportInteraction::NudgeSpeed::Slow);
|
|
});
|
|
addAction(action);
|
|
}
|
|
|
|
// Nudge down fast
|
|
{
|
|
QAction* action = new QAction("Down Fast", this);
|
|
action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Down));
|
|
QObject::connect(action,
|
|
&QAction::triggered,
|
|
[this]()
|
|
{
|
|
m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Down, ViewportInteraction::NudgeSpeed::Fast);
|
|
});
|
|
addAction(action);
|
|
}
|
|
|
|
// Nudge left
|
|
{
|
|
QAction* action = new QAction("Left", this);
|
|
action->setShortcut(QKeySequence(Qt::Key_Left));
|
|
QObject::connect(action,
|
|
&QAction::triggered,
|
|
[this]()
|
|
{
|
|
m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Left, ViewportInteraction::NudgeSpeed::Slow);
|
|
});
|
|
addAction(action);
|
|
}
|
|
|
|
// Nudge left fast
|
|
{
|
|
QAction* action = new QAction("Left Fast", this);
|
|
action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Left));
|
|
QObject::connect(action,
|
|
&QAction::triggered,
|
|
[this]()
|
|
{
|
|
m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Left, ViewportInteraction::NudgeSpeed::Fast);
|
|
});
|
|
addAction(action);
|
|
}
|
|
|
|
// Nudge right
|
|
{
|
|
QAction* action = new QAction("Right", this);
|
|
action->setShortcut(QKeySequence(Qt::Key_Right));
|
|
QObject::connect(action,
|
|
&QAction::triggered,
|
|
[this]()
|
|
{
|
|
m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Right, ViewportInteraction::NudgeSpeed::Slow);
|
|
});
|
|
addAction(action);
|
|
}
|
|
|
|
// Nudge right fast
|
|
{
|
|
QAction* action = new QAction("Right Fast", this);
|
|
action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Right));
|
|
QObject::connect(action,
|
|
&QAction::triggered,
|
|
[this]()
|
|
{
|
|
m_viewportInteraction->Nudge(ViewportInteraction::NudgeDirection::Right, ViewportInteraction::NudgeSpeed::Fast);
|
|
});
|
|
addAction(action);
|
|
}
|
|
|
|
// Give the viewport focus and activate the space bar
|
|
{
|
|
QAction* action = new QAction("Viewport Focus", this);
|
|
action->setShortcut(QKeySequence(Qt::Key_Space));
|
|
QObject::connect(action,
|
|
&QAction::triggered,
|
|
[this]()
|
|
{
|
|
setFocus();
|
|
m_viewportInteraction->ActivateSpaceBar();
|
|
});
|
|
addAction(action);
|
|
}
|
|
}
|
|
|
|
void ViewportWidget::ApplyRulerVisibility()
|
|
{
|
|
// Since we are using a grid layout, setting the width of the corner widget (the square at the top left of the grid)
|
|
// determines whether the rulers are zero size or not.
|
|
int rulerBreadth = (m_rulersVisible) ? RulerWidget::GetRulerBreadth() : 0;
|
|
m_rulerCorner->setFixedSize(rulerBreadth, rulerBreadth);
|
|
}
|
|
|
|
|
|
#include <moc_ViewportWidget.cpp>
|