Commit 6fa7f38a authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Merge branch 'master' of invent.kde.org:multimedia/kdenlive

parents 38e4d06b 6c60a1a7
Pipeline #26587 failed with stage
in 2 minutes and 50 seconds
......@@ -820,6 +820,11 @@
<label>index of current guides overlay for project monitor.</label>
<default>0</default>
</entry>
<entry name="alwaysShowMonitorAudio" type="Bool">
<label>Always display audio thumbs in clip monitor.</label>
<default>false</default>
</entry>
<entry name="displayAudioOverlay" type="Bool">
<label>Show audio overlay info on monitor.</label>
......
......@@ -113,6 +113,7 @@ GLWidget::GLWidget(int id, QObject *parent)
} else if (!(KdenliveSettings::displayProjectMonitorInfo() & 0x01)) {
m_rulerHeight = 0;
}
m_displayRulerHeight = m_rulerHeight;
setPersistentOpenGLContext(true);
setPersistentSceneGraph(true);
......@@ -226,7 +227,7 @@ void GLWidget::initializeGL()
void GLWidget::resizeGL(int width, int height)
{
int x, y, w, h;
height -= m_rulerHeight;
height -= m_displayRulerHeight;
double this_aspect = (double)width / height;
// Special case optimization to negate odd effect of sample aspect ratio
......@@ -518,7 +519,7 @@ void GLWidget::paintGL()
f->glDisable(GL_BLEND);
f->glDisable(GL_DEPTH_TEST);
f->glDepthMask(GL_FALSE);
f->glViewport(0, (m_rulerHeight * devicePixelRatio() * 0.5 + 0.5), width, height);
f->glViewport(0, (m_displayRulerHeight * devicePixelRatio() * 0.5 + 0.5), width, height);
check_error(f);
f->glClearColor(m_bgColor.redF(), m_bgColor.greenF(), m_bgColor.blueF(), 0);
f->glClear(GL_COLOR_BUFFER_BIT);
......@@ -642,18 +643,10 @@ void GLWidget::slotZoom(bool zoomIn)
}
}
void GLWidget::wheelEvent(QWheelEvent *event)
void GLWidget::updateRulerHeight(int addedHeight)
{
if (((event->modifiers() & Qt::ControlModifier) != 0u) && ((event->modifiers() & Qt::ShiftModifier) != 0u)) {
slotZoom(event->delta() > 0);
return;
}
if ((event->modifiers() & Qt::ControlModifier) != 0u) {
QQuickView::wheelEvent(event);
return;
}
emit mouseSeek(event->delta(), (uint)event->modifiers());
event->accept();
m_displayRulerHeight = m_rulerHeight > 0 ? QFontInfo(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)).pixelSize() * 1.5 + addedHeight : 0;
resizeGL(width(), height());
}
void GLWidget::requestSeek(int position)
......@@ -1884,6 +1877,7 @@ void GLWidget::updateScaling()
void GLWidget::switchRuler(bool show)
{
m_rulerHeight = show ? QFontInfo(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)).pixelSize() * 1.5 : 0;
m_displayRulerHeight = m_rulerHeight;
resizeGL(width(), height());
m_proxy->rulerHeightChanged();
}
......@@ -147,7 +147,6 @@ public:
protected:
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
/** @brief Update producer, should ONLY be called from monitor */
int setProducer(const std::shared_ptr<Mlt::Producer> &producer, bool isActive, int position);
int setProducer(const QString &file);
......@@ -197,7 +196,10 @@ protected:
std::shared_ptr<Mlt::Consumer> m_consumer;
std::shared_ptr<Mlt::Producer> m_producer;
int m_id;
/** @brief The height of the qml ruler */
int m_rulerHeight;
/** @brief The height of the qml ruler and audio thumbs */
int m_displayRulerHeight;
QColor m_bgColor;
private:
......@@ -258,6 +260,8 @@ protected:
SharedFrame m_sharedFrame;
QOpenGLContext *m_shareContext;
/** @brief adjust monitor ruler size (for example if we want to display audio thumbs permanently) */
void updateRulerHeight(int addedHeight);
bool acquireSharedFrameTextures();
void bindShaderProgram();
void createGPUAccelFragmentProg();
......
......@@ -628,6 +628,16 @@ void Monitor::setupMenu(QMenu *goMenu, QMenu *overlayMenu, QAction *playZone, QA
QAction *setThumbFrame =
m_contextMenu->addAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("Set current image as thumbnail"), this, SLOT(slotSetThumbFrame()));
m_configMenu->addAction(setThumbFrame);
QAction *alwaysShowAudio =
new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-show-audiothumb")), i18n("Always show audio thumbnails"), this);
alwaysShowAudio->setCheckable(true);
connect(alwaysShowAudio, &QAction::triggered, [this](bool checked) {
KdenliveSettings::setAlwaysShowMonitorAudio(checked);
m_glMonitor->rootObject()->setProperty("permanentAudiothumb", checked);
});
alwaysShowAudio->setChecked(KdenliveSettings::alwaysShowMonitorAudio());
m_contextMenu->addAction(alwaysShowAudio);
m_configMenu->addAction(alwaysShowAudio);
}
if (overlayMenu) {
......@@ -677,9 +687,10 @@ void Monitor::slotForceSize(QAction *a)
case 50:
// resize full size
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
profileHeight += m_glMonitor->m_displayRulerHeight;
m_videoWidget->setMinimumSize(profileWidth, profileHeight);
m_videoWidget->setMaximumSize(profileWidth, profileHeight);
setMinimumSize(QSize(profileWidth, profileHeight + m_toolbar->height() + m_glMonitor->getControllerProxy()->rulerHeight()));
setMinimumSize(QSize(profileWidth, profileHeight + m_toolbar->height()));
break;
default:
// Free resize
......@@ -1028,12 +1039,18 @@ void Monitor::keyPressEvent(QKeyEvent *event)
void Monitor::slotMouseSeek(int eventDelta, uint modifiers)
{
if ((modifiers & Qt::ShiftModifier) != 0u) {
if ((modifiers & Qt::ControlModifier) != 0u) {
// Shift + Ctrl wheel zooms monitor
m_glMonitor->slotZoom(eventDelta > 0);
return;
}
// Shift wheel seeks one second
int delta = qRound(pCore->getCurrentFps());
if (eventDelta > 0) {
delta = 0 - delta;
delta = -delta;
}
delta = qMax(0, m_glMonitor->getCurrentPos() - delta);
m_glMonitor->getControllerProxy()->setPosition(qMin(delta, m_glMonitor->duration() - 1));
delta = qBound(0, m_glMonitor->getCurrentPos() + delta, m_glMonitor->duration() - 1);
m_glMonitor->getControllerProxy()->setPosition(delta);
} else if ((modifiers & Qt::AltModifier) != 0u) {
if (eventDelta >= 0) {
emit seekToPreviousSnap();
......
......@@ -55,6 +55,16 @@ int MonitorProxy::rulerHeight() const
return q->m_rulerHeight;
}
void MonitorProxy::setRulerHeight(int addedHeight)
{
q->updateRulerHeight(addedHeight);
}
void MonitorProxy::seek(int delta, uint modifiers)
{
q->mouseSeek(delta, modifiers);
}
int MonitorProxy::overlayType() const
{
return (q->m_id == (int)Kdenlive::ClipMonitor ? KdenliveSettings::clipMonitorOverlayGuides() : KdenliveSettings::projectMonitorOverlayGuides());
......@@ -308,24 +318,24 @@ QString MonitorProxy::toTimecode(int frames) const
void MonitorProxy::setClipProperties(int clipId, ClipType::ProducerType type, bool hasAV, const QString clipName)
{
if (clipId != m_clipId) {
m_clipId = clipId;
emit clipIdChanged();
}
if (hasAV != m_hasAV) {
m_hasAV = hasAV;
emit clipHasAVChanged();
}
if (type != m_clipType) {
m_clipType = type;
emit clipTypeChanged();
}
if (clipName == m_clipName) {
m_clipName.clear();
emit clipNameChanged();
}
m_clipName = clipName;
emit clipNameChanged();
if (type != m_clipType) {
m_clipType = type;
emit clipTypeChanged();
}
if (clipId != m_clipId) {
m_clipId = clipId;
emit clipIdChanged();
}
}
void MonitorProxy::setAudioThumb(const QList <QUrl> thumbPath)
......
......@@ -42,7 +42,7 @@ class MonitorProxy : public QObject
Q_PROPERTY(int seekFinished MEMBER m_seekFinished NOTIFY seekFinishedChanged)
Q_PROPERTY(int zoneIn READ zoneIn WRITE setZoneIn NOTIFY zoneChanged)
Q_PROPERTY(int zoneOut READ zoneOut WRITE setZoneOut NOTIFY zoneChanged)
Q_PROPERTY(int rulerHeight READ rulerHeight NOTIFY rulerHeightChanged)
Q_PROPERTY(int rulerHeight READ rulerHeight WRITE setRulerHeight NOTIFY rulerHeightChanged)
Q_PROPERTY(QString markerComment READ markerComment NOTIFY markerCommentChanged)
Q_PROPERTY(QList <QUrl> audioThumb MEMBER m_audioThumb NOTIFY audioThumbChanged)
Q_PROPERTY(int overlayType READ overlayType WRITE setOverlayType NOTIFY overlayTypeChanged)
......@@ -71,6 +71,7 @@ public:
* */
int getPosition() const;
Q_INVOKABLE bool setPosition(int pos);
Q_INVOKABLE void seek(int delta, uint modifiers);
void positionFromConsumer(int pos, bool playing);
void setMarkerComment(const QString &comment);
int zoneIn() const;
......@@ -93,6 +94,7 @@ public:
void setClipProperties(int clipId, ClipType::ProducerType type, bool hasAV, const QString clipName);
void setAudioThumb(const QList <QUrl> thumbPath = QList <QUrl>());
void setAudioStream(const QString &name);
void setRulerHeight(int height);
signals:
void positionChanged(int);
......
......@@ -20,6 +20,7 @@
***************************************************************************/
#include "qmlmanager.h"
#include "kdenlivesettings.h"
#include <QFontDatabase>
#include <QQmlContext>
......@@ -113,13 +114,16 @@ void QmlManager::setScene(Kdenlive::MonitorId id, MonitorSceneType type, QSize p
m_view->setSource(QUrl(QStringLiteral("qrc:/qml/kdenlivemonitorripple.qml")));
root = m_view->rootObject();
break;
default:
m_view->setSource(
default: m_view->setSource(
QUrl(id == Kdenlive::ClipMonitor || id == Kdenlive::DvdMonitor ? QStringLiteral("qrc:/qml/kdenliveclipmonitor.qml") : QStringLiteral("qrc:/qml/kdenlivemonitor.qml")));
root = m_view->rootObject();
root->setProperty("profile", QPoint(profile.width(), profile.height()));
root->setProperty("scalex", scalex);
root->setProperty("scaley", scaley);
if (id == Kdenlive::ClipMonitor) {
// Apply the always show audio setting
root->setProperty("permanentAudiothumb", KdenliveSettings::alwaysShowMonitorAudio());
}
break;
}
if (root && duration > 0) {
......
......@@ -12,11 +12,55 @@ Rectangle {
property double rulerZoomWidth: root.zoomFactor * width
// The pixel offset
property double rulerZoomOffset: root.zoomStart * width / root.zoomFactor
property int playheadPosition: controller.position
Rectangle {
color: activePalette.light
width: parent.width
height: 1
}
Timer {
id: scrollTimer
interval: 200; running: false;
onTriggered: {
if (rulerMouseArea.pressed) {
// Check if seeking ruler
var pos = Math.max(rulerMouseArea.mouseX, 0)
root.mouseRulerPos = pos
controller.position = Math.min((pos + ruler.rulerZoomOffset) / root.timeScale, root.duration);
} else if (root.showAudiothumb) {
// Check if seeking audio thumbnail zone
root.updateScrolling()
}
}
}
onPlayheadPositionChanged: {
if (root.zoomFactor == 1) {
return
}
var scaledPosition = ruler.playheadPosition * root.timeScale - ruler.rulerZoomOffset
if (scaledPosition < root.baseUnit) {
if (scaledPosition < 0) {
zoomBar.x = Math.max(0, zoomBar.x + (scaledPosition * root.zoomFactor ) - (zoomBar.width / 2))
root.zoomStart = zoomBar.x / zoomHandleContainer.width
} else {
zoomBar.x = Math.max(0, zoomBar.x - root.baseUnit * root.zoomFactor)
root.zoomStart = zoomBar.x / zoomHandleContainer.width
scrollTimer.start()
}
} else if (scaledPosition > zoomHandleContainer.width - root.baseUnit) {
if (scaledPosition > zoomHandleContainer.width) {
zoomBar.x = Math.min(zoomHandleContainer.width - zoomBar.width, zoomBar.x + (scaledPosition * root.zoomFactor) - (zoomBar.width / 2))
root.zoomStart = zoomBar.x / zoomHandleContainer.width
} else {
zoomBar.x = Math.min(zoomHandleContainer.width - zoomBar.width, zoomBar.x + root.baseUnit * root.zoomFactor)
root.zoomStart = zoomBar.x / zoomHandleContainer.width
scrollTimer.start()
}
}
}
function zoomInRuler(xPos)
{
......@@ -60,8 +104,13 @@ Rectangle {
// Zoom bar container
Rectangle {
id: zoomContainer
SystemPalette { id: barPalette; colorGroup: SystemPalette.Disabled }
height: root.baseUnit
color: activePalette.base
property bool hoveredBar: zoomArea.containsMouse || zoomArea.pressed || zoomStart.isActive || zoomEnd.isActive
color: hoveredBar ? barPalette.text : activePalette.dark
border.color: activePalette.dark
border.width: 1
visible: root.showZoomBar
onVisibleChanged: {
root.zoomOffset = visible ? height : 0
......@@ -71,16 +120,38 @@ Rectangle {
right: parent.right
bottom: parent.top
}
MouseArea {
anchors.fill: parent
onWheel: {
if (wheel.modifiers & Qt.ControlModifier) {
if (wheel.angleDelta.y < 0) {
// zoom out
zoomOutRuler(wheel.x)
} else {
// zoom in
zoomInRuler(wheel.x)
}
} else {
if (wheel.angleDelta.y < 0) {
var newPos = Math.min(zoomHandleContainer.width - zoomBar.width, zoomBar.x + 10)
zoomBar.x = newPos
} else {
var newPos = Math.max(0, zoomBar.x - 10)
zoomBar.x = newPos
}
root.zoomStart = zoomBar.x / zoomHandleContainer.width
}
}
}
Item {
id: zoomHandleContainer
property int previousX: 0
property int previousWidth: zoomHandleContainer.width
anchors.fill: parent
anchors.margins: 3
anchors.margins: 1
Rectangle {
id: zoomBar
radius: height / 2
color: (zoomArea.containsMouse || zoomArea.pressed) ? activePalette.highlight : activePalette.text
color: zoomContainer.hoveredBar ? activePalette.highlight : barPalette.text
height: parent.height
width: parent.width
MouseArea {
......@@ -121,12 +192,22 @@ Rectangle {
// zoom in
zoomInRuler(wheel.x)
}
} else {
if (wheel.angleDelta.y < 0) {
var newPos = Math.min(zoomHandleContainer.width - zoomBar.width, zoomBar.x + 10)
zoomBar.x = newPos
} else {
var newPos = Math.max(0, zoomBar.x - 10)
zoomBar.x = newPos
}
root.zoomStart = zoomBar.x / zoomHandleContainer.width
}
}
}
}
MouseArea {
id: zoomStart
property bool isActive: zoomStart.containsMouse || zoomStart.pressed
anchors.left: zoomBar.left
anchors.leftMargin: - root.baseUnit / 2
anchors.bottom: zoomBar.bottom
......@@ -136,9 +217,11 @@ Rectangle {
cursorShape: Qt.SizeHorCursor
onPressed: {
anchors.left = undefined
startHandleRect.anchors.fill = undefined
}
onReleased: {
anchors.left = zoomBar.left
startHandleRect.anchors.fill = zoomStart
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
......@@ -148,11 +231,24 @@ Rectangle {
zoomBar.x = updatedPos
root.zoomStart = updatedPos / zoomHandleContainer.width
root.zoomFactor = zoomBar.width / zoomHandleContainer.width
startHandleRect.x = mouseX
}
}
Rectangle {
id: startHandleRect
anchors.fill: parent
radius: height / 2
color: zoomStart.isActive ? activePalette.text : barPalette.light
Rectangle {
anchors.fill: parent
anchors.leftMargin: height / 2
color: parent.color
}
}
}
MouseArea {
id: zoomEnd
property bool isActive: zoomEnd.containsMouse || zoomEnd.pressed
anchors.left: zoomBar.right
anchors.leftMargin: - root.baseUnit / 2
anchors.bottom: zoomBar.bottom
......@@ -162,9 +258,11 @@ Rectangle {
cursorShape: Qt.SizeHorCursor
onPressed: {
anchors.left = undefined
endHandleRect.anchors.fill = undefined
}
onReleased: {
anchors.left = zoomBar.right
endHandleRect.anchors.fill = zoomEnd
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
......@@ -172,6 +270,18 @@ Rectangle {
updatedPos = Math.max(updatedPos, zoomBar.x + root.baseUnit / 2)
zoomBar.width = updatedPos - zoomBar.x
root.zoomFactor = zoomBar.width / zoomHandleContainer.width
endHandleRect.x = mouseX
}
}
Rectangle {
id: endHandleRect
anchors.fill: parent
radius: height / 2
color: zoomEnd.isActive ? activePalette.text : barPalette.light
Rectangle {
anchors.fill: parent
anchors.rightMargin: height / 2
color: parent.color
}
}
}
......@@ -277,6 +387,7 @@ Rectangle {
MouseArea {
id: rulerMouseArea
anchors.fill: parent
propagateComposedEvents: true
hoverEnabled: true
onPressed: {
if (mouse.buttons === Qt.LeftButton) {
......@@ -302,6 +413,8 @@ Rectangle {
// zoom in
zoomInRuler(wheel.x)
}
} else {
wheel.accepted = false
}
}
}
......@@ -350,7 +463,7 @@ Rectangle {
opacity: 1
anchors.top: ruler.top
fillColor: activePalette.windowText
x: controller.position * root.timeScale - (width / 2) - ruler.rulerZoomOffset
x: controller.position * root.timeScale - ruler.rulerZoomOffset - (width / 2)
}
Rectangle {
id: trimIn
......
......@@ -32,7 +32,10 @@ Item {
property bool showTimecode: false
property bool showFps: false
property bool showSafezone: false
// Display hover audio thumbnails overlay
property bool showAudiothumb: false
// Always display audio thumbs under video
property bool permanentAudiothumb: false
property bool showToolbar: false
property string clipName: controller.clipName
property real baseUnit: fontMetrics.font.pixelSize
......@@ -44,6 +47,7 @@ Item {
property color overlayColor: 'cyan'
property bool isClipMonitor: true
property int dragType: 0
property int overlayMargin: (audioThumb.stateVisible && !audioThumb.isAudioClip && audioThumb.visible) ? (audioThumb.height + root.zoomOffset) : root.zoomOffset
FontMetrics {
id: fontMetrics
......@@ -57,6 +61,16 @@ Item {
signal editCurrentMarker()
function updateScrolling()
{
if (thumbMouseArea.pressed) {
var pos = Math.max(thumbMouseArea.mouseX, 0)
pos += audioThumb.width/root.zoomFactor * root.zoomStart
controller.setPosition(Math.min(pos / root.timeScale, root.duration));
}
}
onDurationChanged: {
clipMonitorRuler.updateRuler()
// Reset zoom on clip change
......@@ -72,6 +86,28 @@ Item {
// Animate clip name
clipNameLabel.opacity = 1
showAnimate.restart()
// adjust monitor image size if audio thumb is displayed
if (audioThumb.stateVisible && root.permanentAudiothumb && audioThumb.visible) {
controller.rulerHeight = audioThumb.height + root.zoomOffset
} else {
controller.rulerHeight = root.zoomOffset
}
}
onZoomOffsetChanged: {
if (audioThumb.stateVisible && root.permanentAudiothumb && audioThumb.visible) {
controller.rulerHeight = audioThumb.height + root.zoomOffset
} else {
controller.rulerHeight = root.zoomOffset
}
}
onHeightChanged: {
if (audioThumb.stateVisible && root.permanentAudiothumb && audioThumb.visible) {
controller.rulerHeight = audioThumb.height + root.zoomOffset
} else {
controller.rulerHeight = root.zoomOffset
}
}
function updatePalette() {
......@@ -92,7 +128,11 @@ Item {
hoverEnabled: true
acceptedButtons: Qt.NoButton
anchors.fill: parent
onWheel: {
controller.seek(wheel.angleDelta.x + wheel.angleDelta.y, wheel.modifiers)
}
}
SceneToolBar {
id: sceneToolBar
barContainsMouse: sceneToolBar.rightSide ? barOverArea.mouseX >= x - 10 : barOverArea.mouseX < x + width + 10
......@@ -118,6 +158,7 @@ Item {
width: root.profile.x * root.scalex
height: root.profile.y * root.scaley
anchors.centerIn: parent
anchors.verticalCenterOffset : (root.permanentAudiothumb && audioThumb.visible) ? -(audioThumb.height + root.zoomOffset) / 2 : -root.zoomOffset / 2
Loader {
anchors.fill: parent
......@@ -146,7 +187,7 @@ Item {
Item {
id: audioThumb
property bool stateVisible: (clipMonitorRuler.containsMouse || thumbMouseArea.containsMouse || thumbTimer.running || root.showZoomBar)
property bool stateVisible: (root.permanentAudiothumb || clipMonitorRuler.containsMouse || thumbMouseArea.containsMouse || dragZone.opacity == 1 || thumbTimer.running || root.showZoomBar)
property bool isAudioClip: controller.clipType == ProducerType.Audio
anchors {
left: parent.left
......@@ -171,7 +212,15 @@ Item {
height: isAudioClip ? parent.height : parent.height / 6
//font.pixelSize * 3
width: parent.width
visible: root.showAudiothumb && (isAudioClip || controller.clipType == ProducerType.AV)
visible: (root.permanentAudiothumb || root.showAudiothumb) && (isAudioClip || controller.clipType == ProducerType.AV)
onStateVisibleChanged: {
// adjust monitor image size
if (stateVisible && root.permanentAudiothumb && audioThumb.visible) {
controller.rulerHeight = audioThumb.height + root.zoomOffset
} else {
controller.rulerHeight = root.zoomOffset
}
}
states: [
State { when: audioThumb.stateVisible || audioThumb.isAudioClip;
......@@ -183,8 +232,8 @@ Item {
NumberAnimation { property: "opacity"; duration: audioThumb.isAudioClip ? 0 : 500}
} ]
Rectangle {
color: activePalette.base
opacity: audioThumb.isAudioClip ? 1 : 0.6
color: activePalette.dark
opacity: audioThumb.isAudioClip || root.permanentAudiothumb ? 1 : 0.6
anchors.fill: parent
}