viewerwindow: implemented displaying a viewer image fullscreen on another monitor

Implements an extra widget to display images on a separate monitor. Both
viewers have separate display color controls since different monitors may
conform to different standards/transforms.
This commit is contained in:
itsmattkc
2020-04-01 19:21:25 +11:00
parent 6ea6dd810e
commit ed48d24cbc
8 changed files with 298 additions and 50 deletions
+2
View File
@@ -28,5 +28,7 @@ set(OLIVE_SOURCES
widget/viewer/viewerglwidget.cpp
widget/viewer/viewersizer.h
widget/viewer/viewersizer.cpp
widget/viewer/viewerwindow.h
widget/viewer/viewerwindow.cpp
PARENT_SCOPE
)
+1 -1
View File
@@ -13,7 +13,7 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) :
audio_node_ = new AudioInput();
viewer_node_ = new ViewerOutput();
connect(gl_widget_, &ViewerGLWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag);
connect(main_gl_widget(), &ViewerGLWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag);
controls_->SetAudioVideoDragButtonsVisible(true);
connect(controls_, &PlaybackControls::VideoPressed, this, &FootageViewerWidget::StartVideoDrag);
+135 -42
View File
@@ -21,8 +21,10 @@
#include "viewer.h"
#include <QDateTime>
#include <QGuiApplication>
#include <QLabel>
#include <QResizeEvent>
#include <QScreen>
#include <QtMath>
#include <QVBoxLayout>
@@ -55,11 +57,12 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
sizer_ = new ViewerSizer();
stack_->addWidget(sizer_);
gl_widget_ = new ViewerGLWidget();
connect(gl_widget_, &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
connect(gl_widget_, &ViewerGLWidget::CursorColor, this, &ViewerWidget::CursorColor);
connect(sizer_, &ViewerSizer::RequestMatrix, gl_widget_, &ViewerGLWidget::SetMatrix);
sizer_->SetWidget(gl_widget_);
ViewerGLWidget* main_widget = new ViewerGLWidget();
connect(main_widget, &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
connect(main_widget, &ViewerGLWidget::CursorColor, this, &ViewerWidget::CursorColor);
connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerGLWidget::SetMatrix);
sizer_->SetWidget(main_widget);
gl_widgets_.append(main_widget);
// Create waveform view when audio is connected and video isn't
waveform_view_ = new AudioWaveformView();
@@ -145,12 +148,18 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
SizeChangedSlot(n->video_params().width(), n->video_params().height());
LengthChangedSlot(n->Length());
ColorManager* using_manager;
if (override_color_manager_) {
gl_widget_->ConnectColorManager(override_color_manager_);
using_manager = override_color_manager_;
} else if (n->parent()) {
gl_widget_->ConnectColorManager(static_cast<Sequence*>(n->parent())->project()->color_manager());
using_manager = static_cast<Sequence*>(n->parent())->project()->color_manager();
} else {
qWarning() << "Failed to find a suitable color manager for the connected viewer node";
using_manager = nullptr;
}
foreach (ViewerGLWidget* glw, gl_widgets_) {
glw->ConnectColorManager(using_manager);
}
divider_ = CalculateDivider();
@@ -181,7 +190,9 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
// Effectively disables the viewer and clears the state
SizeChangedSlot(0, 0);
gl_widget_->DisconnectColorManager();
foreach (ViewerGLWidget* glw, gl_widgets_) {
glw->DisconnectColorManager();
}
waveform_view_->ConnectTimelinePoints(nullptr);
}
@@ -213,6 +224,16 @@ void ViewerWidget::resizeEvent(QResizeEvent *event)
UpdateMinimumScale();
}
const QList<ViewerGLWidget*> &ViewerWidget::gl_widgets() const
{
return gl_widgets_;
}
ViewerGLWidget *ViewerWidget::main_gl_widget() const
{
return gl_widgets_.first();
}
void ViewerWidget::TogglePlayPause()
{
if (IsPlaying()) {
@@ -249,7 +270,9 @@ void ViewerWidget::SetOverrideSize(int width, int height)
void ViewerWidget::SetMatrix(const QMatrix4x4 &mat)
{
gl_widget_->SetMatrix(mat);
foreach (ViewerGLWidget* glw, gl_widgets_) {
glw->SetMatrix(mat);
}
}
VideoRenderBackend *ViewerWidget::video_renderer() const
@@ -260,12 +283,12 @@ VideoRenderBackend *ViewerWidget::video_renderer() const
void ViewerWidget::UpdateTextureFromNode(const rational& time)
{
if (!GetConnectedNode() || time >= GetConnectedNode()->Length()) {
gl_widget_->SetImage(QString());
main_gl_widget()->SetImage(QString());
} else {
QString frame_fn = video_renderer_->GetCachedFrame(time);
if (!frame_fn.isEmpty()) {
gl_widget_->SetImage(frame_fn);
main_gl_widget()->SetImage(frame_fn);
}
}
}
@@ -294,7 +317,7 @@ void ViewerWidget::PlayInternal(int speed)
controls_->ShowPauseButton();
if (stack_->currentWidget() == sizer_) {
connect(gl_widget_, &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
connect(main_gl_widget(), &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
} else {
connect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate);
}
@@ -325,7 +348,7 @@ int ViewerWidget::CalculateDivider()
{
if (GetConnectedNode() && Config::Current()["AutoSelectDivider"].toBool()) {
int long_side_of_video = qMax(GetConnectedNode()->video_params().width(), GetConnectedNode()->video_params().height());
int long_side_of_widget = qMax(gl_widget_->width(), gl_widget_->height());
int long_side_of_widget = qMax(main_gl_widget()->width(), main_gl_widget()->height());
return qMax(1, long_side_of_video / long_side_of_widget);
}
@@ -356,6 +379,35 @@ void ViewerWidget::UpdateStack()
}
}
void ViewerWidget::SetFullScreenDisplay(QAction *action)
{
QScreen* target = QGuiApplication::screens().at(action->data().toInt());
ViewerWindow* vw = new ViewerWindow(this);
vw->showFullScreen();
vw->setGeometry(target->geometry());
vw->gl_widget()->ConnectColorManager(main_gl_widget()->color_manager());
main_gl_widget()->ConnectSibling(vw->gl_widget());
connect(vw, &ViewerWindow::destroyed, this, &ViewerWidget::WindowAboutToClose);
connect(vw->gl_widget(), &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
if (GetConnectedNode()) {
vw->SetResolution(GetConnectedNode()->video_params().width(), GetConnectedNode()->video_params().height());
}
windows_.append(vw);
gl_widgets_.append(vw->gl_widget());
}
void ViewerWidget::WindowAboutToClose()
{
ViewerWindow* vw = static_cast<ViewerWindow*>(sender());
windows_.removeAll(vw);
gl_widgets_.removeAll(vw->gl_widget());
}
void ViewerWidget::UpdateRendererParameters()
{
if (!GetConnectedNode()) {
@@ -385,40 +437,42 @@ void ViewerWidget::UpdateRendererParameters()
void ViewerWidget::ShowContextMenu(const QPoint &pos)
{
QMenu menu(this);
QMenu menu(static_cast<QWidget*>(sender()));
context_menu_widget_ = static_cast<ViewerGLWidget*>(sender());
// Color options
if (gl_widget_->color_manager() && color_menu_enabled_) {
QStringList displays = gl_widget_->color_manager()->ListAvailableDisplays();
if (context_menu_widget_->color_manager() && color_menu_enabled_) {
QStringList displays = context_menu_widget_->color_manager()->ListAvailableDisplays();
QMenu* ocio_display_menu = menu.addMenu(tr("Display"));
connect(ocio_display_menu, &QMenu::triggered, this, &ViewerWidget::ColorDisplayChanged);
connect(ocio_display_menu, &QMenu::triggered, this, &ViewerWidget::ContextMenuOCIODisplay);
foreach (const QString& d, displays) {
QAction* action = ocio_display_menu->addAction(d);
action->setCheckable(true);
action->setChecked(gl_widget_->ocio_display() == d);
action->setChecked(context_menu_widget_->ocio_display() == d);
action->setData(d);
}
QStringList views = gl_widget_->color_manager()->ListAvailableViews(gl_widget_->ocio_display());
QStringList views = context_menu_widget_->color_manager()->ListAvailableViews(context_menu_widget_->ocio_display());
QMenu* ocio_view_menu = menu.addMenu(tr("View"));
connect(ocio_view_menu, &QMenu::triggered, this, &ViewerWidget::ColorViewChanged);
connect(ocio_view_menu, &QMenu::triggered, this, &ViewerWidget::ContextMenuOCIOView);
foreach (const QString& v, views) {
QAction* action = ocio_view_menu->addAction(v);
action->setCheckable(true);
action->setChecked(gl_widget_->ocio_view() == v);
action->setChecked(context_menu_widget_->ocio_view() == v);
action->setData(v);
}
QStringList looks = gl_widget_->color_manager()->ListAvailableLooks();
QStringList looks = context_menu_widget_->color_manager()->ListAvailableLooks();
QMenu* ocio_look_menu = menu.addMenu(tr("Look"));
connect(ocio_look_menu, &QMenu::triggered, this, &ViewerWidget::ColorLookChanged);
connect(ocio_look_menu, &QMenu::triggered, this, &ViewerWidget::ContextMenuOCIOLook);
QAction* no_look_action = ocio_look_menu->addAction(tr("(None)"));
no_look_action->setCheckable(true);
no_look_action->setChecked(gl_widget_->ocio_look().isEmpty());
no_look_action->setChecked(context_menu_widget_->ocio_look().isEmpty());
foreach (const QString& l, looks) {
QAction* action = ocio_look_menu->addAction(l);
action->setCheckable(true);
action->setChecked(gl_widget_->ocio_look() == l);
action->setChecked(context_menu_widget_->ocio_look() == l);
action->setData(l);
}
@@ -434,6 +488,13 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos)
}
connect(playback_resolution_menu, &QMenu::triggered, this, &ViewerWidget::SetDividerFromMenu);
foreach (QAction* a, playback_resolution_menu->actions()) {
a->setCheckable(true);
if (a->data() == divider_) {
a->setChecked(true);
}
}
// Viewer Zoom Level
QMenu* zoom_menu = menu.addMenu(tr("Zoom"));
int zoom_levels[] = {10, 25, 50, 75, 100, 150, 200, 400};
@@ -443,12 +504,18 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos)
}
connect(zoom_menu, &QMenu::triggered, this, &ViewerWidget::SetZoomFromMenu);
foreach (QAction* a, playback_resolution_menu->actions()) {
a->setCheckable(true);
if (a->data() == divider_) {
a->setChecked(true);
}
// Full Screen Menu
QMenu* full_screen_menu = menu.addMenu(tr("Full Screen"));
for (int i=0;i<QGuiApplication::screens().size();i++) {
QScreen* s = QGuiApplication::screens().at(i);
QAction* a = full_screen_menu->addAction(tr("Screen %1: %2x%3").arg(QString::number(i),
QString::number(s->size().width()),
QString::number(s->size().height())));
a->setData(i);
}
connect(full_screen_menu, &QMenu::triggered, this, &ViewerWidget::SetFullScreenDisplay);
menu.exec(static_cast<QWidget*>(sender())->mapToGlobal(pos));
}
@@ -466,7 +533,7 @@ void ViewerWidget::Pause()
controls_->ShowPlayButton();
if (stack_->currentWidget() == sizer_) {
disconnect(gl_widget_, &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
disconnect(main_gl_widget(), &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
} else {
disconnect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate);
}
@@ -514,27 +581,49 @@ void ViewerWidget::ShuttleRight()
void ViewerWidget::SetOCIOParameters(const QString &display, const QString &view, const QString &look)
{
gl_widget_->SetOCIOParameters(display, view, look);
SetOCIOParameters(display, view, look, main_gl_widget());
}
void ViewerWidget::SetOCIOParameters(const QString &display, const QString &view, const QString &look, ViewerGLWidget* sender)
{
sender->SetOCIOParameters(display, view, look);
}
void ViewerWidget::SetOCIODisplay(const QString &display)
{
gl_widget_->SetOCIODisplay(display);
SetOCIODisplay(display, main_gl_widget());
}
void ViewerWidget::SetOCIODisplay(const QString &display, ViewerGLWidget* sender)
{
sender->SetOCIODisplay(display);
}
void ViewerWidget::SetOCIOView(const QString &view)
{
gl_widget_->SetOCIOView(view);
SetOCIOView(view, main_gl_widget());
}
void ViewerWidget::SetOCIOLook(const QString &look)
{
gl_widget_->SetOCIOLook(look);
SetOCIOLook(look, main_gl_widget());
}
void ViewerWidget::SetOCIOView(const QString &view, ViewerGLWidget* sender)
{
sender->SetOCIOView(view);
}
void ViewerWidget::SetOCIOLook(const QString &look, ViewerGLWidget* sender)
{
sender->SetOCIOLook(look);
}
void ViewerWidget::SetSignalCursorColorEnabled(bool e)
{
gl_widget_->SetSignalCursorColorEnabled(e);
foreach (ViewerGLWidget* glw, gl_widgets_) {
glw->SetSignalCursorColorEnabled(e);
}
}
void ViewerWidget::TimebaseChangedEvent(const rational &timebase)
@@ -576,6 +665,10 @@ void ViewerWidget::RendererCachedTime(const rational &time, qint64 job_time)
void ViewerWidget::SizeChangedSlot(int width, int height)
{
sizer_->SetChildSize(width, height);
foreach (ViewerWindow* vw, windows_) {
vw->SetResolution(width, height);
}
}
void ViewerWidget::LengthChangedSlot(const rational &length)
@@ -585,19 +678,19 @@ void ViewerWidget::LengthChangedSlot(const rational &length)
UpdateMinimumScale();
}
void ViewerWidget::ColorDisplayChanged(QAction* action)
void ViewerWidget::ContextMenuOCIODisplay(QAction* action)
{
SetOCIODisplay(action->data().toString());
SetOCIODisplay(action->data().toString(), context_menu_widget_);
}
void ViewerWidget::ColorViewChanged(QAction *action)
void ViewerWidget::ContextMenuOCIOView(QAction *action)
{
SetOCIOView(action->data().toString());
SetOCIOView(action->data().toString(), context_menu_widget_);
}
void ViewerWidget::ColorLookChanged(QAction *action)
void ViewerWidget::ContextMenuOCIOLook(QAction *action)
{
SetOCIOLook(action->data().toString());
SetOCIOLook(action->data().toString(), context_menu_widget_);
}
void ViewerWidget::SetDividerFromMenu(QAction *action)
+22 -5
View File
@@ -36,6 +36,7 @@
#include "render/backend/audio/audiobackend.h"
#include "viewerglwidget.h"
#include "viewersizer.h"
#include "viewerwindow.h"
#include "widget/playbackcontrols/playbackcontrols.h"
#include "widget/timebased/timebased.h"
@@ -132,10 +133,11 @@ protected:
OpenGLBackend* video_renderer_;
AudioBackend* audio_renderer_;
ViewerGLWidget* gl_widget_;
PlaybackControls* controls_;
const QList<ViewerGLWidget *> &gl_widgets() const;
ViewerGLWidget* main_gl_widget() const;
private:
void UpdateTimeInternal(int64_t i);
@@ -149,6 +151,11 @@ private:
void UpdateMinimumScale();
void SetOCIOParameters(const QString& display, const QString& view, const QString& look, ViewerGLWidget* sender);
void SetOCIODisplay(const QString& display, ViewerGLWidget* sender);
void SetOCIOView(const QString& view, ViewerGLWidget* sender);
void SetOCIOLook(const QString& look, ViewerGLWidget* sender);
QStackedWidget* stack_;
ViewerSizer* sizer_;
@@ -172,6 +179,12 @@ private:
AudioWaveformView* waveform_view_;
QList<ViewerWindow*> windows_;
QList<ViewerGLWidget*> gl_widgets_;
ViewerGLWidget* context_menu_widget_;
private slots:
void PlaybackTimerUpdate();
@@ -188,17 +201,17 @@ private slots:
/**
* @brief Slot called whenever this viewer's OCIO display setting has changed
*/
void ColorDisplayChanged(QAction* action);
void ContextMenuOCIODisplay(QAction* action);
/**
* @brief Slot called whenever this viewer's OCIO view setting has changed
*/
void ColorViewChanged(QAction* action);
void ContextMenuOCIOView(QAction* action);
/**
* @brief Slot called whenever this viewer's OCIO look setting has changed
*/
void ColorLookChanged(QAction* action);
void ContextMenuOCIOLook(QAction* action);
void SetDividerFromMenu(QAction* action);
@@ -208,6 +221,10 @@ private slots:
void UpdateStack();
void SetFullScreenDisplay(QAction* action);
void WindowAboutToClose();
};
#endif // VIEWER_WIDGET_H
+36 -2
View File
@@ -114,6 +114,8 @@ void ViewerGLWidget::SetImage(const QString &fn)
texture_.Upload(load_buffer_.data());
emit LoadedBuffer(&load_buffer_);
doneCurrent();
has_image_ = true;
@@ -128,12 +130,16 @@ void ViewerGLWidget::SetImage(const QString &fn)
}
update();
if (has_image_) {
emit LoadedBuffer(&load_buffer_);
} else {
emit LoadedBuffer(nullptr);
}
}
void ViewerGLWidget::SetSignalCursorColorEnabled(bool e)
{
qDebug() << e;
signal_cursor_color_ = e;
setMouseTracking(e);
@@ -149,6 +155,28 @@ void ViewerGLWidget::SetSignalCursorColorEnabled(bool e)
}
}
void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer)
{
has_image_ = in_buffer;
if (has_image_) {
makeCurrent();
if (!texture_.IsCreated()
|| texture_.width() != in_buffer->width()
|| texture_.height() != in_buffer->height()
|| texture_.format() != in_buffer->format()) {
texture_.Create(context(), in_buffer->width(), in_buffer->height(), in_buffer->format(), in_buffer->data());
} else {
texture_.Upload(in_buffer->data());
}
doneCurrent();
}
update();
}
void ViewerGLWidget::SetOCIODisplay(const QString &display)
{
ocio_display_ = display;
@@ -198,6 +226,12 @@ const QString &ViewerGLWidget::ocio_look() const
return ocio_look_;
}
void ViewerGLWidget::ConnectSibling(ViewerGLWidget *sibling)
{
connect(this, &ViewerGLWidget::LoadedBuffer, sibling, &ViewerGLWidget::SetImageFromLoadBuffer, Qt::QueuedConnection);
sibling->SetImageFromLoadBuffer(&load_buffer_);
}
void ViewerGLWidget::mousePressEvent(QMouseEvent *event)
{
QOpenGLWidget::mousePressEvent(event);
+17
View File
@@ -81,6 +81,10 @@ public:
const QString& ocio_view() const;
const QString& ocio_look() const;
const QMatrix4x4& GetMatrix();
void ConnectSibling(ViewerGLWidget* sibling);
public slots:
/**
* @brief Set the texture to draw and draw it
@@ -130,6 +134,14 @@ public slots:
*/
void SetSignalCursorColorEnabled(bool e);
/**
* @brief Overrides the image with the load buffer of another ViewerGLWidget
*
* If there are multiple ViewerGLWidgets showing the same thing, this is faster than decoding the image from file
* each time.
*/
void SetImageFromLoadBuffer(Frame* in_buffer);
signals:
/**
* @brief Signal emitted when the user starts dragging from the viewer
@@ -141,6 +153,11 @@ signals:
*/
void CursorColor(const Color& reference, const Color& display);
/**
* @brief Connect this to the SetImageFromLoadBuffer() slot of another ViewerGLWidget to show the same thing
*/
void LoadedBuffer(Frame* load_buffer);
protected:
/**
* @brief Override the mouse press event simply to emit the DragStarted() signal
+55
View File
@@ -0,0 +1,55 @@
#include "viewerwindow.h"
#include <QKeyEvent>
#include <QVBoxLayout>
ViewerWindow::ViewerWindow(QWidget *parent) :
QWidget(parent, Qt::Window | Qt::WindowStaysOnTopHint)
{
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->setSpacing(0);
gl_widget_ = new ViewerGLWidget();
layout->addWidget(gl_widget_);
}
ViewerGLWidget *ViewerWindow::gl_widget() const
{
return gl_widget_;
}
void ViewerWindow::SetResolution(int width, int height)
{
// Set GL widget matrix to maintain this texture's aspect ratio
double window_ar = static_cast<double>(this->width()) / static_cast<double>(this->height());
double image_ar = static_cast<double>(width) / static_cast<double>(height);
QMatrix4x4 mat;
if (window_ar > image_ar) {
// Window is wider than image, adjust X scale
mat.scale(image_ar / window_ar, 1.0f, 1.0f);
} else if (window_ar < image_ar) {
// Window is taller than image, adjust Y scale
mat.scale(1.0f, window_ar / image_ar, 1.0f);
}
gl_widget_->SetMatrix(mat);
}
void ViewerWindow::keyPressEvent(QKeyEvent *e)
{
QWidget::keyPressEvent(e);
if (e->key() == Qt::Key_Escape) {
close();
}
}
void ViewerWindow::closeEvent(QCloseEvent *e)
{
QWidget::closeEvent(e);
deleteLater();
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef VIEWERWINDOW_H
#define VIEWERWINDOW_H
#include <QWidget>
#include "viewerglwidget.h"
class ViewerWindow : public QWidget
{
public:
ViewerWindow(QWidget* parent = nullptr);
ViewerGLWidget* gl_widget() const;
/**
* @brief Used to adjust resulting picture to be the right aspect ratio
*/
void SetResolution(int width, int height);
protected:
virtual void keyPressEvent(QKeyEvent* e) override;
virtual void closeEvent(QCloseEvent* e) override;
private:
ViewerGLWidget* gl_widget_;
};
#endif // VIEWERWINDOW_H