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,43 @@
/*
* 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 <AzQtComponents/AzQtComponentsAPI.h>
#include <QSettings>
#include <QString>
namespace AzQtComponents
{
// Use this class to begin and end a group of settings with scope resolution.
//
class AutoSettingsGroup
{
public:
AutoSettingsGroup(QSettings* settings, const QString& groupName)
: m_settings(settings)
{
m_settings->beginGroup(groupName);
}
~AutoSettingsGroup()
{
m_settings->endGroup();
m_settings->sync();
}
private:
QSettings* m_settings;
};
} // namespace AzQtComponents
@@ -0,0 +1,70 @@
/*
* 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 <AzQtComponents/Utilities/ColorUtilities.h>
#include <AzQtComponents/Utilities/Conversions.h>
#include <AzQtComponents/Components/Style.h>
#include <cmath>
#include <QPainter>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Math/MathUtils.h>
#include <assert.h>
namespace AzQtComponents
{
QColor AdjustGamma(const QColor& color, qreal gamma)
{
const QColor rgb = color.toRgb();
const float r = aznumeric_cast<float>(std::pow(rgb.redF(), 1.0 / gamma));
const float g = aznumeric_cast<float>(std::pow(rgb.greenF(), 1.0 / gamma));
const float b = aznumeric_cast<float>(std::pow(rgb.blueF(), 1.0 / gamma));
const float a = aznumeric_cast<float>(rgb.alphaF());
return toQColor(r, g, b, a);
}
AZ::Color AdjustGamma(const AZ::Color& color, float gamma)
{
const float r = std::pow(color.GetR(), 1.0f / gamma);
const float g = std::pow(color.GetG(), 1.0f / gamma);
const float b = std::pow(color.GetB(), 1.0f / gamma);
const float a = color.GetA();
return AZ::Color(r, g, b, a);
}
QBrush MakeAlphaBrush(const QColor& color, qreal gamma)
{
QColor adjusted = gamma == 1.0f ? color : AdjustGamma(color, gamma);
QPixmap alpha = Style::cachedPixmap(QStringLiteral(":/stylesheet/img/UI20/alpha-background.png"));
QPainter filler(&alpha);
filler.fillRect(alpha.rect(), adjusted);
return QBrush(alpha);
}
QBrush MakeAlphaBrush(const AZ::Color& color, float gamma)
{
return MakeAlphaBrush(ToQColor(color), gamma);
}
bool AreClose(const AZ::Color& left, const AZ::Color& right)
{
// the two values can't be off more than a single 8 bit rounded unit
const float tolerance = 1.0f / 255.0f;
return left.IsClose(right, tolerance);
}
} // namespace AzQtComponents
@@ -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 <AzQtComponents/AzQtComponentsAPI.h>
#include <AzCore/Math/Color.h>
#include <QColor>
#include <QBrush>
namespace AzQtComponents
{
AZ_QT_COMPONENTS_API QColor AdjustGamma(const QColor& color, qreal gamma);
AZ_QT_COMPONENTS_API AZ::Color AdjustGamma(const AZ::Color& color, float gamma);
/**
* Make a pixmap brush from the alpha-checking pattern with the color we're interested filled over the top
* This is useful for drawRoundedRect calls because it prevents anti-aliasing and overlapped drawing issues
* without losing us anti-aliasing of and corner radius
*/
AZ_QT_COMPONENTS_API QBrush MakeAlphaBrush(const QColor& color, qreal gamma = 1.0f);
AZ_QT_COMPONENTS_API QBrush MakeAlphaBrush(const AZ::Color& color, float gamma = 1.0f);
AZ_QT_COMPONENTS_API bool AreClose(const AZ::Color& left, const AZ::Color& right);
};
@@ -0,0 +1,89 @@
/*
* 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 <AzQtComponents/Utilities/Conversions.h>
#include <AzCore/Math/MathUtils.h>
#include <QLocale>
namespace AzQtComponents
{
namespace Internal
{
template <typename FloatType>
float clamp(FloatType channel)
{
return AZ::GetClamp(static_cast<float>(channel), 0.0f, 1.0f);
};
}
using namespace Internal;
QColor toQColor(const AZ::Color& color)
{
return QColor::fromRgbF(clamp(color.GetR()), clamp(color.GetG()), clamp(color.GetB()), clamp(color.GetA()));
}
QColor toQColor(float r, float g, float b, float a)
{
return QColor::fromRgbF(clamp(r), clamp(g), clamp(b), clamp(a));
}
AZ::Color fromQColor(const QColor& color)
{
const QColor rgb = color.toRgb();
return AZ::Color(static_cast<float>(rgb.redF()), static_cast<float>(rgb.greenF()), static_cast<float>(rgb.blueF()), static_cast<float>(rgb.alphaF()));
}
QString toString(double value, int numDecimals, const QLocale& locale, bool showGroupSeparator)
{
const QChar decimalPoint = locale.decimalPoint();
const QChar zeroDigit = locale.zeroDigit();
// We want to truncate, not round. toString will round, so we add an extra decimal place to the formatting
// so we can remove the last value
QString retValue = locale.toString(value, 'f', (numDecimals > 0) ? numDecimals + 1 : 0);
// Handle special cases when we have decimals in our value
if (numDecimals > 0)
{
// Truncate the extra digit now, if it's still there
int decimalPointIndex = retValue.lastIndexOf(decimalPoint);
if ((decimalPointIndex > 0) && (retValue.size() - (decimalPointIndex + 1)) == (numDecimals + 1))
{
retValue.resize(retValue.size() - 1);
}
// Remove trailing zeros, since the locale conversion won't do
// it for us
QString trailingZeros = QString("%1+$").arg(zeroDigit);
retValue.remove(QRegExp(trailingZeros));
// It's possible we could be left with a decimal point on the end
// if we stripped the trailing zeros, so if that's the case, then
// add a zero digit on the end so that it is obvious that this is
// a float value
if (retValue.endsWith(decimalPoint))
{
retValue.append(zeroDigit);
}
}
// Copied from the QDoubleSpinBox sub-class to handle removing the
// group separator if necessary
if (!showGroupSeparator && qAbs(value) >= 1000.0)
{
retValue.remove(locale.groupSeparator());
}
return retValue;
}
} // namespace AzQtComponents
@@ -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 <AzQtComponents/AzQtComponentsAPI.h>
#include <AzCore/Math/Color.h>
#include <QColor>
#include <QString>
class QLocale;
namespace AzQtComponents
{
AZ_QT_COMPONENTS_API QColor toQColor(const AZ::Color& color);
AZ_QT_COMPONENTS_API QColor toQColor(float r, float g, float b, float a = 1.0f);
AZ_QT_COMPONENTS_API AZ::Color fromQColor(const QColor& color);
AZ_QT_COMPONENTS_API QString toString(double value, int numDecimals, const QLocale& locale, bool showGroupSeparator = false);
// Maintained for backwards compile compatibility
inline QColor ToQColor(const AZ::Color& color)
{
return toQColor(color);
}
// Maintained for backwards compile compatibility
inline AZ::Color FromQColor(const QColor& color)
{
return fromQColor(color);
}
} // namespace AzQtComponents
@@ -0,0 +1,65 @@
/*
* 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 <AzQtComponents/Utilities/DesktopUtilities.h>
#include <QDir>
#include <QProcess>
namespace AzQtComponents
{
void ShowFileOnDesktop(const QString& path)
{
#if defined(AZ_PLATFORM_WINDOWS)
// Launch explorer at the path provided
QStringList args;
if (!QFileInfo(path).isDir())
{
// Folders are just opened, files are selected
args << "/select,";
}
args << QDir::toNativeSeparators(path);
QProcess::startDetached("explorer", args);
#else
if (QFileInfo(path).isDir())
{
QProcess::startDetached("/usr/bin/osascript", { "-e",
QStringLiteral("tell application \"Finder\" to open(\"%1\" as POSIX file)").arg(QDir::toNativeSeparators(path)) });
}
else
{
QProcess::startDetached("/usr/bin/osascript", { "-e",
QStringLiteral("tell application \"Finder\" to reveal POSIX file \"%1\"").arg(QDir::toNativeSeparators(path)) });
}
QProcess::startDetached("/usr/bin/osascript", { "-e",
QStringLiteral("tell application \"Finder\" to activate") });
#endif
}
QString fileBrowserActionName()
{
#ifdef AZ_PLATFORM_WINDOWS
const char* exploreActionName = "Open in Explorer";
#elif defined(AZ_PLATFORM_MAC)
const char* exploreActionName = "Open in Finder";
#else
const char* exploreActionName = "Open in file browser";
#endif
return QObject::tr(exploreActionName);
}
}
@@ -0,0 +1,23 @@
/*
* 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 <AzQtComponents/AzQtComponentsAPI.h>
#include <QString>
namespace AzQtComponents
{
AZ_QT_COMPONENTS_API void ShowFileOnDesktop(const QString& path);
AZ_QT_COMPONENTS_API QString fileBrowserActionName();
};
@@ -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 <AzQtComponents/Utilities/HandleDpiAwareness.h>
namespace AzQtComponents
{
namespace Utilities
{
namespace Platform
{
void HandleDpiAwareness(DpiAwareness dpiAwareness);
}
void HandleDpiAwareness(DpiAwareness dpiAwareness)
{
Platform::HandleDpiAwareness(dpiAwareness);
}
} // namespace Utilities
} // namespace AZ
@@ -0,0 +1,29 @@
/*
* 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 <AzQtComponents/AzQtComponentsAPI.h>
namespace AzQtComponents
{
namespace Utilities
{
enum DpiAwareness {
Unset,
Unaware,
SystemDpiAware,
PerScreenDpiAware
};
AZ_QT_COMPONENTS_API void HandleDpiAwareness(DpiAwareness dpiAwareness);
} // namespace Utilities
} // namespace AZ
@@ -0,0 +1,27 @@
/*
* 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 <QObject>
namespace AzQtComponents
{
class MouseHider
: public QObject
{
public:
explicit MouseHider(QObject* parent = nullptr);
~MouseHider() override;
};
} // namespace AzQtComponents
@@ -0,0 +1,29 @@
/*
* 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 "MouseHider.h"
namespace AzQtComponents
{
MouseHider::MouseHider(QObject* parent /* = nullptr */)
: QObject(parent)
{
//TODO: Implement for Linux
}
MouseHider::~MouseHider()
{
//TODO: Implement for Linux
}
} // namespace AzQtComponents
@@ -0,0 +1,31 @@
/*
* 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 "MouseHider.h"
#include <CoreGraphics/CoreGraphics.h>
namespace AzQtComponents
{
MouseHider::MouseHider(QObject* parent /* = nullptr */)
: QObject(parent)
{
CGDisplayHideCursor(kCGDirectMainDisplay);
}
MouseHider::~MouseHider()
{
CGDisplayShowCursor(kCGDirectMainDisplay);
}
} // namespace AzQtComponents
@@ -0,0 +1,31 @@
/*
* 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 <AzCore/PlatformIncl.h>
#include <AzQtComponents/Utilities/MouseHider.h>
namespace AzQtComponents
{
MouseHider::MouseHider(QObject* parent /* = nullptr */)
: QObject(parent)
{
ShowCursor(false);
}
MouseHider::~MouseHider()
{
ShowCursor(true);
}
} // namespace AzQtComponents
@@ -0,0 +1,102 @@
/*
* 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 <AzCore/PlatformIncl.h>
#include <AzQtComponents/Utilities/QtPluginPaths.h>
#include <QApplication>
#include <QDir>
#include <QSettings>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QFile>
#include <QJsonValue>
#include <AzCore/base.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#if defined(__APPLE__)
// needed for _NSGetExecutablePath
#include <mach-o/dyld.h>
#include <libgen.h>
#include <unistd.h>
#endif
#if defined(AZ_PLATFORM_LINUX)
#include <libgen.h>
#include <unistd.h>
#endif
namespace AzQtComponents
{
// the purpose of this function is to set up the QT globals so that it finds its platform libraries and that kind of thing.
// these paths have to be set up BEFORE you create the Qt application itself, otherwise it won't know how to work on your current platform
// since it will be missing the plugin for your current platform (windows/osx/etc)
void PrepareQtPaths()
{
#if !defined(USE_DEFAULT_QT_LIBRARY_PATHS)
char executablePath[AZ_MAX_PATH_LEN];
AZ::Utils::GetExecutablePathReturnType result = AZ::Utils::GetExecutablePath(executablePath, AZ_MAX_PATH_LEN);
if (result.m_pathStored == AZ::Utils::ExecutablePathResult::Success)
{
if (result.m_pathIncludesFilename)
{
char* lastSlashAddress = strrchr(executablePath, AZ_CORRECT_FILESYSTEM_SEPARATOR);
if (lastSlashAddress == executablePath)
{
executablePath[1] = '\0'; //Executable directory is root, therefore set the following character to \0
}
else
{
*lastSlashAddress = '\0';
}
}
QApplication::addLibraryPath(executablePath);
}
else
{
QApplication::addLibraryPath(".");
}
#endif
}
QString FindEngineRootDir(QApplication* app)
{
// The QApplication must be initialized before this method is called
// so it must be passed in as a parameter, even if we don't use it.
(void)app;
// Attempt to locate the engine by looking for 'engineroot.txt' and walking up the folder path until it is found (or not)
QDir appPath(QApplication::applicationDirPath());
QString engineRootPath;
while (!appPath.isRoot())
{
if (QFile::exists(appPath.filePath("engine.json")))
{
engineRootPath = appPath.absolutePath();
break;
}
if (!appPath.cdUp())
{
break;
}
}
return engineRootPath;
}
} // namespace AzQtComponents
@@ -0,0 +1,28 @@
/*
* 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 <AzQtComponents/AzQtComponentsAPI.h>
#include <QString>
class QApplication;
namespace AzQtComponents
{
AZ_QT_COMPONENTS_API void PrepareQtPaths();
AZ_QT_COMPONENTS_API QString FindEngineRootDir(QApplication* app);
} // namespace AzQtComponents
@@ -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 "QtViewPaneEffects.h"
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <QGraphicsEffect>
#include <QWidget>
#include <QVariant>
namespace AzQtComponents
{
static const char* s_viewPaneManagerDisableEffectPropertyName = "QtViewPaneManagerDisableEffect";
static const float s_disabledPaneOpacity = 0.4f;
// put the widget into a grayed out state to indicate it cannot be interacted with
static void DisableViewPaneDisabledGraphicsEffect(QWidget* widget)
{
// only remove effects from widgets that we added them to
QGraphicsEffect* effect = widget->graphicsEffect();
if (effect != nullptr && !effect->property(s_viewPaneManagerDisableEffectPropertyName).isNull())
{
widget->setGraphicsEffect(nullptr);
}
}
// force the widget to be repainted
static void ForceWidgetRedraw(QWidget* widget)
{
widget->hide();
widget->resize(widget->size());
widget->show();
}
// restore the widget to its normal state to show it can now be interacted with
static void EnableViewPaneDisabledGraphicsEffect(QWidget* widget)
{
// only apply effects to widgets that didn't have them already...
if (widget->graphicsEffect() == nullptr)
{
auto effect = AZStd::make_unique<QGraphicsOpacityEffect>();
effect->setOpacity(s_disabledPaneOpacity);
// flag this as our effect so we can get rid of it later
effect->setProperty(s_viewPaneManagerDisableEffectPropertyName, true);
widget->setGraphicsEffect(effect.release());
// ensure the widget is redrawn with the updated graphical effect
ForceWidgetRedraw(widget);
}
}
void SetWidgetInteractEnabled(QWidget* widget, const bool on)
{
widget->setEnabled(on);
if (on)
{
DisableViewPaneDisabledGraphicsEffect(widget);
}
else
{
EnableViewPaneDisabledGraphicsEffect(widget);
}
}
} // namespace AzQtComponents
@@ -0,0 +1,26 @@
/*
* 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 <AzQtComponents/AzQtComponentsAPI.h>
class QWidget;
namespace AzQtComponents
{
/// Set the state of all widgets that should be enabled/disabled.
/// When \p on is \p false, the widget is set to disabled and a graphics effect is applied
/// to show the widget as inactive. The reverse of this is applied when \p on is \p true.
AZ_QT_COMPONENTS_API void SetWidgetInteractEnabled(QWidget* widget, bool on);
} // namespace AzQtComponents
@@ -0,0 +1,185 @@
/*
* 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 <AzCore/PlatformIncl.h>
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
#include <AzQtComponents/Utilities/ScreenUtilities.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
#include <QApplication>
#include <QDesktopWidget>
#include <QMainWindow>
#include <QPainter>
#include <QDockWidget>
#include <QCursor>
#include <QScreen>
#include <QTimer>
#include <QWindow>
namespace AzQtComponents
{
QRect GetTotalScreenGeometry()
{
QRect totalScreenRect;
int numScreens = QApplication::screens().count();
for (int i = 0; i < numScreens; ++i)
{
totalScreenRect = totalScreenRect.united(QApplication::screens().at(i)->geometry());
}
return totalScreenRect;
}
void EnsureGeometryWithinScreenTop(QRect& geometry)
{
QScreen* screen = Utilities::ScreenAtPoint(geometry.center());
if (screen)
{
QRect screenRect = screen->geometry();
if (!screenRect.isNull() && geometry.top() < screenRect.top())
{
geometry.moveTop(screenRect.top());
}
}
}
void EnsureWindowWithinScreenGeometry(QWidget* widget)
{
// manipulate the window, not the input widget. Might be the same thing
widget = widget->window();
// get the edges of the screens
QRect screenGeometry = GetTotalScreenGeometry();
// check if we cross over any edges
QRect geometry = widget->geometry();
const bool mustBeEntirelyInside = true;
if (screenGeometry.contains(geometry, mustBeEntirelyInside))
{
return;
}
if ((geometry.x() + geometry.width()) > screenGeometry.right())
{
geometry.moveTo(screenGeometry.right() - geometry.width(), geometry.top());
}
if (geometry.x() < screenGeometry.left())
{
geometry.moveTo(screenGeometry.left(), geometry.top());
}
if ((geometry.y() + geometry.height()) > screenGeometry.bottom())
{
geometry.moveTo(geometry.x(), screenGeometry.bottom() - geometry.height());
}
if (geometry.y() < screenGeometry.top())
{
geometry.moveTo(geometry.x(), screenGeometry.top());
}
widget->setGeometry(geometry);
}
void SetClipRegionForDockingWidgets(QWidget* widget, QPainter& painter, QMainWindow* mainWindow)
{
QRegion clipRegion(widget->rect());
for (QDockWidget* childDockWidget : mainWindow->findChildren<QDockWidget*>(QString(), Qt::FindDirectChildrenOnly))
{
if (childDockWidget->isFloating() && childDockWidget->isVisible() && !childDockWidget->isMinimized())
{
QRect globalDockRect = childDockWidget->geometry();
QRect localDockRect(widget->mapFromGlobal(globalDockRect.topLeft()), globalDockRect.size());
clipRegion = clipRegion.subtracted(QRegion(localDockRect));
}
}
painter.setClipRegion(clipRegion);
painter.setClipping(true);
}
void SetCursorPos(const QPoint& point)
{
const QList<QScreen*> screens = QGuiApplication::screens();
bool finished = false;
for (int screenIndex = 0; !finished && screenIndex < screens.size(); ++screenIndex)
{
QScreen* screen = screens[screenIndex];
if (screen->geometry().contains(point))
{
QCursor::setPos(screen, point);
finished = true;
}
}
}
void SetCursorPos(int x, int y)
{
SetCursorPos(QPoint(x, y));
}
void bringWindowToTop(QWidget* widget)
{
auto window = widget->window();
bool wasMaximized = window->isMaximized();
// this will un-maximize a window that's previously been maximized...
window->setWindowState(Qt::WindowActive);
if (wasMaximized)
{
// re-maximize it now
window->showMaximized();
}
else if (WindowDecorationWrapper* wrapper = qobject_cast<WindowDecorationWrapper*>(window))
{
// otherwise, restore this from any saved settings if we can
wrapper->showFromSettings();
}
else
{
window->show();
}
window->raise();
// activateWindow only works if the window is shown, so let's include
// a slight delay to make sure the events for showing the window
// have been processed.
QTimer::singleShot(0, widget, [widget] {
widget->activateWindow();
if (QWindow* window = widget->window()->windowHandle())
{
window->requestActivate();
}
// The Windows OS has all kinds of issues with bringing a window to the front
// from another application.
// For the moment, we assume that any other application calling this
// has called AllowSetForegroundWindow(), otherwise, the code below
// won't work.
#ifdef Q_OS_WIN
HWND hwnd = reinterpret_cast<HWND>(widget->winId());
SetForegroundWindow(hwnd);
// Hack to get this to show up for sure
::SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
::SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
#endif // Q_OS_WIN
});
}
} // namespace AzQtComponents
@@ -0,0 +1,53 @@
/*
* 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 <AzQtComponents/AzQtComponentsAPI.h>
#include <QOperatingSystemVersion>
#include <QRect>
class QWidget;
class QMainWindow;
class QPainter;
namespace AzQtComponents
{
AZ_QT_COMPONENTS_API QRect GetTotalScreenGeometry();
AZ_QT_COMPONENTS_API void EnsureWindowWithinScreenGeometry(QWidget* widget);
AZ_QT_COMPONENTS_API void EnsureGeometryWithinScreenTop(QRect& geometry);
AZ_QT_COMPONENTS_API void SetClipRegionForDockingWidgets(QWidget* widget, QPainter& painter, QMainWindow* mainWindow);
AZ_QT_COMPONENTS_API void SetCursorPos(const QPoint& point);
AZ_QT_COMPONENTS_API void SetCursorPos(int x, int y);
// Rationale: There are platform-specific differences in how mouse coordinates are handled, this
// lets us sample every pixel of a HiDPI screen running at > "100%" scaling.
struct AZ_QT_COMPONENTS_API MappedPoint
{
QPoint native;
QPoint qt;
};
MappedPoint AZ_QT_COMPONENTS_API MappedCursorPosition();
AZ_QT_COMPONENTS_API void bringWindowToTop(QWidget* widget);
inline bool isWin10()
{
return QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10);
}
} // namespace AzQtComponents
@@ -0,0 +1,26 @@
/*
* 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 <QCursor>
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
namespace AzQtComponents
{
MappedPoint MappedCursorPosition()
{
// We can correctly handle every on-screen pixel on linux
return { QCursor::pos(), QCursor::pos() };
}
} // namespace AzQtComponents
@@ -0,0 +1,26 @@
/*
* 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 <QCursor>
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
namespace AzQtComponents
{
MappedPoint MappedCursorPosition()
{
// We can correctly handle every on-screen pixel on macOS
return { QCursor::pos(), QCursor::pos() };
}
} // namespace AzQtComponents
@@ -0,0 +1,29 @@
/*
* 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 <AzCore/PlatformIncl.h>
#include <QCursor>
#include <AzQtComponents/Utilities/QtWindowUtilities.h>
namespace AzQtComponents
{
MappedPoint MappedCursorPosition()
{
POINT point;
GetCursorPos(&point);
return { { point.x, point.y }, QCursor::pos() };
}
} // namespace AzQtComponents
@@ -0,0 +1,31 @@
/*
* 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 <AzQtComponents/Utilities/RandomNumberGenerator.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AzQtComponents
{
namespace
{
thread_local AZStd::unique_ptr<QRandomGenerator> generator;
} // anonymous namespace
QRandomGenerator* GetRandomGenerator()
{
if (!generator)
{
generator.reset(new QRandomGenerator(QRandomGenerator::system()->generate()));
}
return generator.get();
}
} // namespace AzQtComponents
@@ -0,0 +1,22 @@
/*
* 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 <AzQtComponents/AzQtComponentsAPI.h>
#include <QRandomGenerator>
namespace AzQtComponents
{
AZ_QT_COMPONENTS_API QRandomGenerator* GetRandomGenerator();
} // namespace AzQtComponents
@@ -0,0 +1,75 @@
/*
* 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.
*
*/
#ifndef SCOPEDCLEANUP_H
#define SCOPEDCLEANUP_H
#include <QtCore/qglobal.h>
/**
* RAII-style class that executes a lambda when it goes out of scope.
* Ideal for cleanup in functions that have multiple return points.
*
* Usage:
* #include <AzQtComponents/Utilities/ScopedCleanup.h>
* (...)
* auto cleanup = scopedCleanup([]{ <my cleanup that should be executed at end of scope>});
*/
template <typename F> class ScopedCleanup;
template <typename F> ScopedCleanup<F> scopedCleanup(F f);
template <typename F>
class ScopedCleanup
{
public:
ScopedCleanup(ScopedCleanup&& other) Q_DECL_NOEXCEPT
: m_func(std::move(other.m_func))
, m_invoke(other.m_invoke)
{
other.dismiss();
}
~ScopedCleanup()
{
if (m_invoke)
{
m_func();
}
}
void dismiss() Q_DECL_NOEXCEPT
{
m_invoke = false;
}
private:
explicit ScopedCleanup(F f) Q_DECL_NOEXCEPT
: m_func(std::move(f))
{
}
Q_DISABLE_COPY(ScopedCleanup)
F m_func;
bool m_invoke = true;
friend ScopedCleanup scopedCleanup<F>(F);
};
template <typename F>
ScopedCleanup<F> scopedCleanup(F f)
{
return ScopedCleanup<F>(std::move(f));
}
#endif
@@ -0,0 +1,42 @@
/*
* 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 <QWidget>
#include <QImage>
namespace AzQtComponents
{
class Eyedropper;
class ScreenGrabber
: public QObject
{
Q_OBJECT
public:
class Internal;
public:
explicit ScreenGrabber(const QSize size, Eyedropper* parent = nullptr);
~ScreenGrabber() override;
QImage grab(const QPoint& point) const;
private:
QSize m_size;
Eyedropper* m_owner;
QScopedPointer<Internal> m_internal;
};
} // namespace AzQtComponents
@@ -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.
*
*/
#include <QImage>
#include <QPixmap>
#include <AzQtComponents/Components/Widgets/Eyedropper.h>
#include <AzQtComponents/Utilities/ScreenGrabber.h>
namespace AzQtComponents
{
class ScreenGrabber::Internal
{};
ScreenGrabber::ScreenGrabber(const QSize size, Eyedropper* parent /* = nullptr */)
: QObject(parent)
, m_size(size)
, m_owner(parent)
{
}
ScreenGrabber::~ScreenGrabber()
{
}
QImage ScreenGrabber::grab(const QPoint& point) const
{
QImage empty;
QImage result = QImage();
return result;
}
} // namespace AzQtComponents
#include "Utilities/moc_ScreenGrabber.cpp"
@@ -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 <CoreFoundation/CoreFoundation.h>
#include <AppKit/AppKit.h>
#include <QImage>
#include <QPixmap>
#include <QtMac>
#include <AzQtComponents/Components/Widgets/Eyedropper.h>
#include <AzQtComponents/Utilities/ScreenGrabber.h>
namespace AzQtComponents
{
class ScreenGrabber::Internal
{};
ScreenGrabber::ScreenGrabber(const QSize size, Eyedropper* parent /* = nullptr */)
: QObject(parent)
, m_size(size)
, m_owner(parent)
{
}
ScreenGrabber::~ScreenGrabber()
{
}
QImage ScreenGrabber::grab(const QPoint& point) const
{
WId magnifier = m_owner->effectiveWinId();
NSView* view = reinterpret_cast<NSView*>(magnifier);
CGWindowID windowId = (CGWindowID)[[view window] windowNumber];
CFArrayRef allWindows = CGWindowListCreate(kCGWindowListOptionAll | kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
CFMutableArrayRef windows = CFArrayCreateMutableCopy(nullptr, 0, allWindows);
for (int i = 0; i < CFArrayGetCount(windows); i++)
{
CGWindowID window = static_cast<CGWindowID>(reinterpret_cast<uint64_t>(CFArrayGetValueAtIndex(windows, i)));
if (window == windowId)
{
CFArrayRemoveValueAtIndex(windows, i);
break;
}
}
CFRelease(allWindows);
// The part of the screen we want to scale up
QRect region({}, m_size);
region.moveCenter(point);
CGRect bounds = CGRectMake(region.x(), region.y(), region.width(), region.height());
CGImageRef cgImage = CGWindowListCreateImageFromArray(bounds, windows, kCGWindowImageNominalResolution);
CFRelease(windows);
QImage result = QtMac::fromCGImageRef(cgImage).toImage();
CGImageRelease(cgImage);
return result;
}
} // namespace AzQtComponents
#include "Utilities/moc_ScreenGrabber.cpp"
@@ -0,0 +1,262 @@
/*
* 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 <AzCore/PlatformIncl.h>
#include <Magnification.h>
#include <QApplication>
#include <QScreen>
#include <QWindow>
#include <QtWinExtras/QtWin>
#include <QGlobalStatic>
#include <QHash>
#include <QMutex>
#include <QtMath>
#include <string>
#include <AzCore/Debug/Trace.h>
#include <AzQtComponents/Components/Widgets/Eyedropper.h>
#include <AzQtComponents/Utilities/ScreenGrabber.h>
namespace AzQtComponents
{
namespace
{
std::string GetLastErrorString()
{
DWORD error = GetLastError();
if (!error)
{
return{};
}
char* buffer = nullptr;
DWORD length = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&buffer),
0,
nullptr);
if (!length)
{
return{};
}
std::string result(buffer, length);
LocalFree(buffer);
return result;
}
using Hash = QHash<HWND, ScreenGrabber::Internal*>;
Hash* g_internalInstances = nullptr;
// This is a POD type with size == sizeof(void*)
QBasicMutex g_internalInstancesLock;
}
class ScreenGrabber::Internal
{
public:
Internal(QSize size);
virtual ~Internal();
QWidget* host() const { return m_host.data(); }
HWND magnifier() const { return m_magnifier; }
const QImage captured() const { return m_captured; }
private:
static BOOL WINAPI callback(HWND hwnd, void* srcdata, MAGIMAGEHEADER srcheader, void* destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty);
QScopedPointer<QWidget> m_host;
HWND m_magnifier;
QImage m_captured;
};
ScreenGrabber::Internal::Internal(QSize size)
{
QMutexLocker locker(&g_internalInstancesLock);
if (!g_internalInstances)
{
BOOL success = MagInitialize();
Q_UNUSED(success);
AZ_Assert(success, "Failed to initialize Windows Magnification API: %s", GetLastErrorString().c_str());
}
// Make a window to host the magnification "control"
m_host.reset(new QWidget(nullptr, Qt::FramelessWindowHint));
m_host->setFixedSize(size / m_host->devicePixelRatioF());
m_host->show();
// move it to the top left
m_host->move(0, 0);
HWND hostHandle = reinterpret_cast<HWND>(m_host->effectiveWinId());
// Add the WS_EX_LAYERED extended window style
SetWindowLong(hostHandle, GWL_EXSTYLE, GetWindowLong(hostHandle, GWL_EXSTYLE) | WS_EX_LAYERED);
// Set full opacity
SetLayeredWindowAttributes(hostHandle, 0, 255, LWA_ALPHA);
// Create the magnifier "control". It's really just a HWND that the API dumps pixels into.
// We add 1 pixel to the size after observing that the callback function receives images of
// the correct size, but with garbage data in the last scanline. We crop this in the callback.
m_magnifier = CreateWindowW(
WC_MAGNIFIERW,
L"Lumberyard Color Picker Eyedropper Helper",
WS_CHILD | WS_VISIBLE,
0,
0,
size.width(),
size.height() + 1,
hostHandle, nullptr, nullptr, nullptr);
// HACK; The magnifier control must be "visible" to function, which means it has to have some pixels on
// screen. HOWEVER, we can set a clip region on the parent (layered) window which hides it entirely
// and allows us to have it on screen without being visible.
// We still receive all the pixels in the callback.
RECT hostSize = {};
GetWindowRect(hostHandle, &hostSize);
HRGN clip = CreateRectRgn(hostSize.right - hostSize.left, 0, hostSize.right - hostSize.left, hostSize.bottom - hostSize.top);
SetWindowRgn(hostHandle, clip, true);
if (!g_internalInstances)
{
g_internalInstances = new QHash<HWND, ScreenGrabber::Internal*>();
}
g_internalInstances->insert(m_magnifier, this);
{
// Although this mechanism is deprecated, we need to use it to get the pixel data because the GDI
// functions that QScreen uses to read pixels don't work outside the visible desktop area. Ideally,
// we'd be able to just read the pixels from the magnifier's HWND in the same way, but this just
// results in white pixels due to the special way the magnification API writes the pixels to the
// screen.
BOOL success = MagSetImageScalingCallback(m_magnifier, &callback);
Q_UNUSED(success)
AZ_Assert(success, "Failed to initialize Windows Magnification imaging scaling callback: %s", GetLastErrorString().c_str());
}
}
ScreenGrabber::Internal::~Internal()
{
g_internalInstancesLock.lock();
g_internalInstances->remove(m_magnifier);
if (g_internalInstances->isEmpty())
{
delete g_internalInstances;
g_internalInstances = nullptr;
MagUninitialize();
}
g_internalInstancesLock.unlock();
DestroyWindow(m_magnifier);
}
BOOL ScreenGrabber::Internal::callback(HWND hwnd, void* srcdata, MAGIMAGEHEADER srcheader, void* destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty)
{
Q_UNUSED(srcheader);
Q_UNUSED(destdata);
Q_UNUSED(destheader);
Q_UNUSED(unclipped);
Q_UNUSED(clipped);
Q_UNUSED(dirty);
// We are actually given some Magnification API made child window of our "magnification control".
HWND magnifier = GetAncestor(hwnd, GA_PARENT);
QImage::Format format = QImage::Format_Invalid;
if (srcheader.format == GUID_WICPixelFormat24bppRGB || srcheader.format == GUID_WICPixelFormat24bppBGR)
{
format = QImage::Format_RGB888;
}
else if (srcheader.format == GUID_WICPixelFormat32bppRGBA || srcheader.format == GUID_WICPixelFormat32bppBGRA)
{
format = QImage::Format_RGBA8888;
}
AZ_Assert(format != QImage::Format_Invalid, "Unable to convert pixel format");
// We subtract one from the height because the last scanline is always garbage
auto result = QImage(((unsigned char*)srcdata) + srcheader.offset, srcheader.width, srcheader.height - 1, format);
// With the above hack to make the magnifier window invisible, the R/B channels are inverted vs.
// what the reported pixel format would suggest. If some of the pixels are visible, this doesn't
// appear to be the case.
if (srcheader.format != GUID_WICPixelFormat24bppBGR && srcheader.format != GUID_WICPixelFormat32bppBGRA)
{
result = result.rgbSwapped();
}
// We hold the lock until the end of the function as we don't know what thread we're being
// called back from and the UI thread may delete the instance
QMutexLocker locker(&g_internalInstancesLock);
if (!g_internalInstances)
{
AZ_Warning("ScreenGrabber", false, "Callback for unknown Magnification API control handle");
return false;
}
auto instance = g_internalInstances->find(magnifier);
if (instance == g_internalInstances->end())
{
AZ_Warning("ScreenGrabber", false, "Callback for unknown Magnification API control handle");
return false;
}
(*instance)->m_captured = result;
return true;
}
ScreenGrabber::ScreenGrabber(const QSize size, Eyedropper* parent /* = nullptr */)
: QObject(static_cast<QObject*>(parent))
, m_size(size)
, m_owner(parent)
{
m_internal.reset(new Internal(size));
}
ScreenGrabber::~ScreenGrabber()
{
m_internal.reset();
}
QImage ScreenGrabber::grab(const QPoint& point) const
{
// We have to do this lazily, as we need the owning Eyedropper to have been shown
// before we can get its HWND and exclude it.
HWND hwnd = reinterpret_cast<HWND>(m_owner->effectiveWinId());
BOOL success = MagSetWindowFilterList(m_internal->magnifier(), MW_FILTERMODE_EXCLUDE, 1, &hwnd);
Q_UNUSED(success);
AZ_Assert(success, "Couldn't add the grabber window to the magnification system's window filter list: %s", GetLastErrorString().c_str());
// The part of the screen we want to scale up
QRect region({}, m_size);
region.moveCenter(point);
RECT area{ region.left(), region.top(), region.right(), region.bottom() };
success = MagSetWindowSource(m_internal->magnifier(), area);
Q_UNUSED(success);
AZ_Assert(success, "Couldn't update the part of the screen being magnified: %s", GetLastErrorString().c_str());
// Cause the magnification target to be redrawn
InvalidateRect(m_internal->magnifier(), nullptr, true);
return m_internal->captured();
}
} // namespace AzQtComponents
#include "Utilities/moc_ScreenGrabber.cpp"
@@ -0,0 +1,35 @@
/*
* 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 <AzQtComponents/Utilities/ScreenUtilities.h>
#include <QApplication>
namespace AzQtComponents
{
namespace Utilities
{
//! Return the screen a point lies in, or the primary screen if no screen was found.
QScreen* ScreenAtPoint(const QPoint& point)
{
QScreen* screen = QApplication::screenAt(point);
if (!screen)
{
return QApplication::primaryScreen();
}
return screen;
}
} // namespace Utilities
} // namespace AzQtComponents
@@ -0,0 +1,26 @@
/*
* 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
class QPoint;
class QScreen;
namespace AzQtComponents
{
namespace Utilities
{
QScreen* ScreenAtPoint(const QPoint& point);
} // namespace Utilities
} // namespace AzQtComponents
@@ -0,0 +1,38 @@
/*
* 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 <AzQtComponents/Utilities/QtWindowUtilities.h>
#include <QTextDocument>
#include <QWidget>
namespace AzQtComponents
{
void forceToolTipLineWrap(QWidget* widget)
{
const QString& toolTip = widget->toolTip();
// Qt will only line wrap tooltips if they are rich text / HTML
// so if Qt doesn't think this text is rich text, we throw on some unnecessary
// tags to make it think it is HTML
if (!toolTip.isEmpty() && !Qt::mightBeRichText(toolTip))
{
QString newText = QStringLiteral("<b></b>%1").arg(toolTip);
// if we're switching to HTML, we better handle newlines too
newText = newText.replace('\n', "<br/>");
widget->setToolTip(newText);
}
}
} // namespace AzQtComponents
@@ -0,0 +1,32 @@
/*
* 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 <AzQtComponents/AzQtComponentsAPI.h>
class QWidget;
namespace AzQtComponents
{
/**
Forces the tooltip text on a QWidget to do line wrapping.
Qt will only line wrap tooltips if they are rich text / HTML so if Qt doesn't think this text
is rich text, we throw on some unnecessary <b></b> tags to make it think it is HTML. Also
replaces \n characters with <br/> tags.
*/
AZ_QT_COMPONENTS_API void forceToolTipLineWrap(QWidget* widget);
} // namespace AzQtComponents