Move DolphinQt2 to DolphinQt

This commit is contained in:
spycrab
2018-07-07 00:40:15 +02:00
parent 059880bb16
commit 13ba24c5a6
233 changed files with 392 additions and 392 deletions

View File

@ -0,0 +1,22 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QAction>
#include <QKeySequence>
#include <QString>
// Since we have to support Qt < 5.6, we need our own implementation of addAction(QString&
// text,QObject*,PointerToMemberFunction);
template <typename ParentClass, typename RecieverClass, typename Func>
QAction* AddAction(ParentClass* parent, const QString& text, const RecieverClass* receiver,
Func slot, const QKeySequence& shortcut = 0)
{
QAction* action = parent->addAction(text);
action->setShortcut(shortcut);
action->connect(action, &QAction::triggered, receiver, slot);
return action;
}

View File

@ -0,0 +1,45 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
// Based on:
// https://stackoverflow.com/questions/30005540/keeping-the-aspect-ratio-of-a-sub-classed-qwidget-during-resize
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
#include <QBoxLayout>
#include <QResizeEvent>
AspectRatioWidget::AspectRatioWidget(QWidget* widget, float width, float height, QWidget* parent)
: QWidget(parent), m_ar_width(width), m_ar_height(height)
{
m_layout = new QBoxLayout(QBoxLayout::LeftToRight, this);
// add spacer, then your widget, then spacer
m_layout->addItem(new QSpacerItem(0, 0));
m_layout->addWidget(widget);
m_layout->addItem(new QSpacerItem(0, 0));
}
void AspectRatioWidget::resizeEvent(QResizeEvent* event)
{
float aspect_ratio = static_cast<float>(event->size().width()) / event->size().height();
int widget_stretch, outer_stretch;
if (aspect_ratio > (m_ar_width / m_ar_height)) // too wide
{
m_layout->setDirection(QBoxLayout::LeftToRight);
widget_stretch = height() * (m_ar_width / m_ar_height); // i.e., my width
outer_stretch = (width() - widget_stretch) / 2 + 0.5;
}
else // too tall
{
m_layout->setDirection(QBoxLayout::TopToBottom);
widget_stretch = width() * (m_ar_height / m_ar_width); // i.e., my height
outer_stretch = (height() - widget_stretch) / 2 + 0.5;
}
m_layout->setStretch(0, outer_stretch);
m_layout->setStretch(1, widget_stretch);
m_layout->setStretch(2, outer_stretch);
}

View File

@ -0,0 +1,22 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QWidget>
class QBoxLayout;
class AspectRatioWidget : public QWidget
{
Q_OBJECT
public:
AspectRatioWidget(QWidget* widget, float width, float height, QWidget* parent = nullptr);
void resizeEvent(QResizeEvent* event) override;
private:
QBoxLayout* m_layout;
float m_ar_width;
float m_ar_height;
};

View File

@ -0,0 +1,21 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/QtUtils/BlockUserInputFilter.h"
#include <QEvent>
BlockUserInputFilter* BlockUserInputFilter::Instance()
{
static BlockUserInputFilter s_block_user_input_filter;
return &s_block_user_input_filter;
}
bool BlockUserInputFilter::eventFilter(QObject* object, QEvent* event)
{
const QEvent::Type event_type = event->type();
return event_type == QEvent::KeyPress || event_type == QEvent::KeyRelease ||
event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonRelease ||
event_type == QEvent::MouseButtonDblClick;
}

View File

@ -0,0 +1,20 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QObject>
class QEvent;
class BlockUserInputFilter : public QObject
{
Q_OBJECT
public:
static BlockUserInputFilter* Instance();
private:
BlockUserInputFilter() = default;
bool eventFilter(QObject* object, QEvent* event) override;
};

View File

@ -0,0 +1,15 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <QEvent>
#include "DolphinQt/QtUtils/DoubleClickEventFilter.h"
bool DoubleClickEventFilter::eventFilter(QObject* object, QEvent* event)
{
if (event->type() == QEvent::MouseButtonDblClick)
emit doubleClicked();
return false;
}

View File

@ -0,0 +1,17 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QObject>
class DoubleClickEventFilter : public QObject
{
Q_OBJECT
signals:
void doubleClicked();
private:
bool eventFilter(QObject* object, QEvent* event) override;
};

View File

@ -0,0 +1,40 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/QtUtils/ElidedButton.h"
#include <QFontMetrics>
#include <QStyleOptionButton>
#include <QStylePainter>
ElidedButton::ElidedButton(const QString& text, Qt::TextElideMode elide_mode)
: QPushButton(text, nullptr), m_elide_mode{elide_mode}
{
}
Qt::TextElideMode ElidedButton::elideMode() const
{
return m_elide_mode;
}
void ElidedButton::setElideMode(Qt::TextElideMode elide_mode)
{
if (elide_mode == m_elide_mode)
return;
m_elide_mode = elide_mode;
repaint();
}
void ElidedButton::paintEvent(QPaintEvent* event)
{
QStyleOptionButton option;
initStyleOption(&option);
option.text = fontMetrics().elidedText(
text(), m_elide_mode,
style()->subElementRect(QStyle::SE_PushButtonContents, &option, this).width());
QStylePainter{this}.drawControl(QStyle::CE_PushButton, option);
}

View File

@ -0,0 +1,21 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QPushButton>
class ElidedButton : public QPushButton
{
Q_OBJECT
public:
explicit ElidedButton(const QString& text = QStringLiteral(""),
Qt::TextElideMode elide_mode = Qt::ElideRight);
Qt::TextElideMode elideMode() const;
void setElideMode(Qt::TextElideMode elide_mode);
private:
void paintEvent(QPaintEvent* event) override;
Qt::TextElideMode m_elide_mode;
};

View File

@ -0,0 +1,33 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/QtUtils/ImageConverter.h"
#include <vector>
#include <QPixmap>
#include "Common/CommonTypes.h"
#include "UICommon/GameFile.h"
QPixmap ToQPixmap(const UICommon::GameBanner& banner)
{
return ToQPixmap(banner.buffer, banner.width, banner.height);
}
QPixmap ToQPixmap(const std::vector<u32>& buffer, int width, int height)
{
QImage image(width, height, QImage::Format_RGB888);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
const u32 color = buffer[y * width + x];
image.setPixel(
x, y, qRgb((color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF) >> 0));
}
}
return QPixmap::fromImage(image);
}

View File

@ -0,0 +1,19 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "Common/CommonTypes.h"
class QPixmap;
namespace UICommon
{
struct GameBanner;
}
QPixmap ToQPixmap(const UICommon::GameBanner& banner);
QPixmap ToQPixmap(const std::vector<u32>& buffer, int width, int height);

View File

@ -0,0 +1,20 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QObject>
#include <utility>
// QWidget and subclasses are not thread-safe! However, Qt's signal-slot connection mechanism will
// invoke slots/functions on the correct thread for any object. We can (ab)use this to queue up
// arbitrary code from non-GUI threads. For more information, see:
// https://stackoverflow.com/questions/21646467/
template <typename T, typename F>
static void QueueOnObject(T* obj, F&& func)
{
QObject src;
QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(func), Qt::QueuedConnection);
}

View File

@ -0,0 +1,77 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QCoreApplication>
#include <QEvent>
#include <QPointer>
#include <QThread>
#include <optional>
#include <type_traits>
#include <utility>
#include "Common/Event.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
class QObject;
// QWidget and subclasses are not thread-safe! This helper takes arbitrary code from any thread,
// safely runs it on the appropriate GUI thread, waits for it to finish, and returns the result.
//
// If the target object is destructed before the code gets to run, the QPointer will be nulled and
// the function will return nullopt.
template <typename F>
auto RunOnObject(QObject* object, F&& functor)
{
using OptionalResultT = std::optional<std::result_of_t<F()>>;
// If we queue up a functor on the current thread, it won't run until we return to the event loop,
// which means waiting for it to finish will never complete. Instead, run it immediately.
if (object->thread() == QThread::currentThread())
return OptionalResultT(functor());
class FnInvokeEvent : public QEvent
{
public:
FnInvokeEvent(F&& functor, QObject* obj, Common::Event& event, OptionalResultT& result)
: QEvent(QEvent::None), m_func(std::move(functor)), m_obj(obj), m_event(event),
m_result(result)
{
}
~FnInvokeEvent()
{
if (m_obj)
{
m_result = m_func();
}
else
{
// is already nullopt
}
m_event.Set();
}
private:
F m_func;
QPointer<QObject> m_obj;
Common::Event& m_event;
OptionalResultT& m_result;
};
Common::Event event{};
OptionalResultT result = std::nullopt;
QCoreApplication::postEvent(object,
new FnInvokeEvent(std::forward<F>(functor), object, event, result));
event.Wait();
return result;
}
template <typename Base, typename Type, typename Receiver>
auto RunOnObject(Receiver* obj, Type Base::*func)
{
return RunOnObject(obj, [obj, func] { return (obj->*func)(); });
}

View File

@ -0,0 +1,55 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/QtUtils/SignalDaemon.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <QSocketNotifier>
int SignalDaemon::s_sigterm_fd[2];
static constexpr char message[] =
"\nA signal was received. A second signal will force Dolphin to stop.\n";
SignalDaemon::SignalDaemon(QObject* parent) : QObject(parent)
{
if (socketpair(AF_UNIX, SOCK_STREAM, 0, s_sigterm_fd))
qFatal("Couldn't create TERM socketpair");
m_term = new QSocketNotifier(s_sigterm_fd[1], QSocketNotifier::Read, this);
connect(m_term, &QSocketNotifier::activated, this, &SignalDaemon::OnNotifierActivated);
}
SignalDaemon::~SignalDaemon()
{
close(s_sigterm_fd[0]);
close(s_sigterm_fd[1]);
}
void SignalDaemon::OnNotifierActivated()
{
m_term->setEnabled(false);
char tmp;
if (read(s_sigterm_fd[1], &tmp, sizeof(char)))
{
}
m_term->setEnabled(true);
emit InterruptReceived();
}
void SignalDaemon::HandleInterrupt(int)
{
write(STDERR_FILENO, message, sizeof(message));
char a = 1;
if (write(s_sigterm_fd[0], &a, sizeof(a)))
{
}
}

View File

@ -0,0 +1,31 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QObject>
class QSocketNotifier;
// Loosely based on https://doc.qt.io/qt-5.9/unix-signals.html
class SignalDaemon : public QObject
{
Q_OBJECT
public:
explicit SignalDaemon(QObject* parent);
~SignalDaemon();
static void HandleInterrupt(int);
signals:
void InterruptReceived();
private:
void OnNotifierActivated();
static int s_sigterm_fd[2];
QSocketNotifier* m_term;
};

View File

@ -0,0 +1,94 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/QtUtils/WinIconHelper.h"
#ifdef _WIN32
#include <Windows.h>
// The following code is adapted from qpixmap_win.cpp (c) The Qt Company Ltd. (https://qt.io)
// Licensed under the GNU GPL v3
static inline BITMAPINFO GetBMI(int width, int height, bool topToBottom)
{
BITMAPINFO bmi = {};
auto& bih = bmi.bmiHeader;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = width;
bih.biHeight = topToBottom ? -height : height;
bih.biPlanes = 1;
bih.biBitCount = 32;
bih.biCompression = BI_RGB;
bih.biSizeImage = width * height * 4;
return bmi;
}
static QPixmap PixmapFromHICON(HICON icon)
{
HDC screenDevice = GetDC(0);
HDC hdc = CreateCompatibleDC(screenDevice);
ReleaseDC(0, screenDevice);
ICONINFO iconinfo;
const bool result = GetIconInfo(icon, &iconinfo); // x and y Hotspot describes the icon center
if (!result)
{
qErrnoWarning("QPixmap::fromWinHICON(), failed to GetIconInfo()");
DeleteDC(hdc);
return QPixmap();
}
const int w = iconinfo.xHotspot * 2;
const int h = iconinfo.yHotspot * 2;
BITMAPINFO bitmapInfo = GetBMI(w, h, false);
DWORD* bits;
HBITMAP winBitmap = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, (VOID**)&bits, NULL, 0);
HGDIOBJ oldhdc = reinterpret_cast<HBITMAP>(SelectObject(hdc, winBitmap));
DrawIconEx(hdc, 0, 0, icon, iconinfo.xHotspot * 2, iconinfo.yHotspot * 2, 0, 0, DI_NORMAL);
QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
if (image.isNull())
return {};
BITMAPINFO bmi = GetBMI(w, h, true);
QScopedArrayPointer<uchar> data(new uchar[bmi.bmiHeader.biSizeImage]);
if (!GetDIBits(hdc, winBitmap, 0, h, data.data(), &bmi, DIB_RGB_COLORS))
return {};
for (int y = 0; y < image.height(); ++y)
{
void* dest = static_cast<void*>(image.scanLine(y));
const void* src = data.data() + y * image.bytesPerLine();
memcpy(dest, src, image.bytesPerLine());
}
// dispose resources created by iconinfo call
DeleteObject(iconinfo.hbmMask);
DeleteObject(iconinfo.hbmColor);
SelectObject(hdc, oldhdc); // restore state
DeleteObject(winBitmap);
DeleteDC(hdc);
return QPixmap::fromImage(image);
}
QIcon WinIconHelper::GetNativeIcon()
{
QIcon icon;
for (int size : {16, 32, 48, 256})
{
HANDLE h = LoadImageW(GetModuleHandleW(nullptr), L"\"DOLPHIN\"", IMAGE_ICON, size, size,
LR_CREATEDIBSECTION);
if (h && h != INVALID_HANDLE_VALUE)
{
auto* icon_handle = static_cast<HICON>(h);
icon.addPixmap(PixmapFromHICON(icon_handle));
}
}
return icon;
}
#endif

View File

@ -0,0 +1,16 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#ifdef _WIN32
#include <QIcon>
namespace WinIconHelper
{
QIcon GetNativeIcon();
};
#endif

View File

@ -0,0 +1,19 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <QEvent>
#include <QObject>
#include "DolphinQt/QtUtils/WindowActivationEventFilter.h"
bool WindowActivationEventFilter::eventFilter(QObject* object, QEvent* event)
{
if (event->type() == QEvent::WindowDeactivate)
emit windowDeactivated();
if (event->type() == QEvent::WindowActivate)
emit windowActivated();
return false;
}

View File

@ -0,0 +1,18 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QObject>
class WindowActivationEventFilter : public QObject
{
Q_OBJECT
signals:
void windowActivated();
void windowDeactivated();
private:
bool eventFilter(QObject* object, QEvent* event) override;
};

View File

@ -0,0 +1,63 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
#include <QFrame>
#include <QLayout>
#include <QPalette>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
QWidget* GetWrappedWidget(QWidget* wrapped_widget, QWidget* to_resize, int margin_width,
int margin_height)
{
auto* scroll = new QScrollArea;
scroll->setWidget(wrapped_widget);
scroll->setWidgetResizable(true);
scroll->setFrameStyle(QFrame::NoFrame);
if (to_resize != nullptr)
{
// For some reason width() is bigger than it needs to be.
auto min_size = wrapped_widget->minimumSizeHint();
int recommended_width = min_size.width() + margin_width;
int recommended_height = min_size.height() + margin_height;
to_resize->resize(std::max(recommended_width, to_resize->width()),
std::max(recommended_height, to_resize->height()));
}
#if defined(_WIN32) || defined(__APPLE__)
// Transparency can cause unwanted side-effects on OSes other than Windows / macOS
// Make sure the background color stays consistent with the parent widget
QPalette p = wrapped_widget->palette();
p.setColor(QPalette::Window, QColor(0, 0, 0, 0));
wrapped_widget->setPalette(p);
scroll->setPalette(p);
#endif
return scroll;
}
void WrapInScrollArea(QWidget* parent, QLayout* wrapped_layout, QWidget* to_resize)
{
if (to_resize == nullptr)
to_resize = parent;
auto* widget = new QWidget;
widget->setLayout(wrapped_layout);
auto* scroll_area = GetWrappedWidget(widget, to_resize, 0, 0);
auto* scroll_layout = new QVBoxLayout;
scroll_layout->addWidget(scroll_area);
scroll_layout->setMargin(0);
parent->setLayout(scroll_layout);
}

View File

@ -0,0 +1,14 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
class QLayout;
class QWidget;
QWidget* GetWrappedWidget(QWidget* wrapped_widget, QWidget* to_resize = nullptr,
int margin_width = 50, int margin_height = 50);
// Wrap wrapped_layout in a QScrollArea and fill the parent widget with it
void WrapInScrollArea(QWidget* parent, QLayout* wrapped_layout, QWidget* to_resize = nullptr);