Browse Source

chatgui: вернуть ручной resize с пропорцией (двигает окно, край под мышью) + фикс перемотки

- resize: ручной setGeometry с живой пропорцией (якорь = противоположный край);
  drag — нативный startSystemMove
- перемотка: QProxyStyle SH_Slider_AbsoluteSetButtons=LeftButton вместо сломанного
  SeekSlider (sliderReleased не эмитился — seekMs не вызывался)
v2
evgeny 3 weeks ago
parent
commit
3adf9871fb
  1. 149
      tools/chatgui/src/videoplayer_window.cpp
  2. 11
      tools/chatgui/src/videoplayer_window.h

149
tools/chatgui/src/videoplayer_window.cpp

@ -15,6 +15,7 @@
#include <QApplication> #include <QApplication>
#include <QScreen> #include <QScreen>
#include <QStyle> #include <QStyle>
#include <QProxyStyle>
#include <QWindow> #include <QWindow>
/* ─── VideoRenderWidget ─── */ /* ─── VideoRenderWidget ─── */
@ -52,23 +53,24 @@ static QString fmtTime(qint64 ms) {
return QString("%1:%2").arg(s / 60).arg(s % 60, 2, 10, QChar('0')); return QString("%1:%2").arg(s / 60).arg(s % 60, 2, 10, QChar('0'));
} }
/* Слайдер перемотки: клик по любому месту шкалы = прыжок к позиции (драг кружка сохраняется). */ static QPoint mouseGlobalPos(const QMouseEvent* e) {
class SeekSlider : public QSlider { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
return e->globalPosition().toPoint();
#else
return e->globalPos();
#endif
}
/* Слайдер перемотки: клик по любому месту шкалы = прыжок к позиции (нативно через стиль). */
class SeekSliderStyle : public QProxyStyle {
public: public:
using QSlider::QSlider; using QProxyStyle::QProxyStyle;
protected: int styleHint(StyleHint hint, const QStyleOption* opt, const QWidget* w,
void mousePressEvent(QMouseEvent* e) override { QStyleHintReturn* ret) const override {
if (e->button() == Qt::LeftButton) { if (hint == QStyle::SH_Slider_AbsoluteSetButtons)
setSliderDown(true); return (int)Qt::LeftButton;
int v = QStyle::sliderValueFromPosition(minimum(), maximum(), return QProxyStyle::styleHint(hint, opt, w, ret);
(int)e->position().x(), width());
setValue(v);
emit sliderPressed();
e->accept();
} else {
QSlider::mousePressEvent(e);
}
} }
}; };
@ -100,7 +102,8 @@ VideoPlayerWindow::VideoPlayerWindow(QWidget* parent)
m_playBtn = new QPushButton(QStringLiteral("⏸"), m_controls); m_playBtn = new QPushButton(QStringLiteral("⏸"), m_controls);
m_playBtn->setToolTip(QStringLiteral("Пауза")); m_playBtn->setToolTip(QStringLiteral("Пауза"));
m_slider = new SeekSlider(Qt::Horizontal, m_controls); m_slider = new QSlider(Qt::Horizontal, m_controls);
m_slider->setStyle(new SeekSliderStyle());
m_slider->setRange(0, 0); m_slider->setRange(0, 0);
m_timeLabel = new QLabel(QStringLiteral("0:00 / 0:00"), m_controls); m_timeLabel = new QLabel(QStringLiteral("0:00 / 0:00"), m_controls);
@ -125,11 +128,6 @@ VideoPlayerWindow::VideoPlayerWindow(QWidget* parent)
m_hideTimer->setInterval(2500); m_hideTimer->setInterval(2500);
connect(m_hideTimer, &QTimer::timeout, this, &VideoPlayerWindow::hideControls); connect(m_hideTimer, &QTimer::timeout, this, &VideoPlayerWindow::hideControls);
m_snapTimer = new QTimer(this);
m_snapTimer->setSingleShot(true);
m_snapTimer->setInterval(150);
connect(m_snapTimer, &QTimer::timeout, this, &VideoPlayerWindow::snapToAspect);
connect(m_playBtn, &QPushButton::clicked, this, &VideoPlayerWindow::onPlayPause); connect(m_playBtn, &QPushButton::clicked, this, &VideoPlayerWindow::onPlayPause);
connect(m_closeBtn, &QPushButton::clicked, this, &VideoPlayerWindow::onClose); connect(m_closeBtn, &QPushButton::clicked, this, &VideoPlayerWindow::onClose);
connect(m_slider, &QSlider::sliderPressed, this, &VideoPlayerWindow::onSliderPressed); connect(m_slider, &QSlider::sliderPressed, this, &VideoPlayerWindow::onSliderPressed);
@ -161,7 +159,6 @@ void VideoPlayerWindow::onEngineOpened(int width, int height, qint64 durationMs,
Q_UNUSED(hasAudio); Q_UNUSED(hasAudio);
m_slider->setRange(0, (int)durationMs); m_slider->setRange(0, (int)durationMs);
updateTimeLabels(0); updateTimeLabels(0);
m_videoAspect = (height > 0) ? (double)width / height : 0.0;
QScreen* scr = QApplication::primaryScreen(); QScreen* scr = QApplication::primaryScreen();
QRect ag = scr ? scr->availableGeometry() : QRect(0, 0, 1280, 720); QRect ag = scr ? scr->availableGeometry() : QRect(0, 0, 1280, 720);
@ -312,8 +309,7 @@ void VideoPlayerWindow::mousePressEvent(QMouseEvent* e) {
} }
ResizeMode m = hitTestResize(e->pos()); ResizeMode m = hitTestResize(e->pos());
if (m != ResizeNone) { if (m != ResizeNone) {
m_resizeEdges = edgesForMode(m); beginResize(m, mouseGlobalPos(e));
if (windowHandle()) windowHandle()->startSystemResize(m_resizeEdges);
} else { } else {
if (windowHandle()) windowHandle()->startSystemMove(); if (windowHandle()) windowHandle()->startSystemMove();
} }
@ -325,25 +321,44 @@ void VideoPlayerWindow::mouseMoveEvent(QMouseEvent* e) {
showControls(); showControls();
return; return;
} }
if (m_resizeMode != ResizeNone && (e->buttons() & Qt::LeftButton)) {
applyResize(mouseGlobalPos(e));
return;
}
if (!(e->buttons() & Qt::LeftButton)) if (!(e->buttons() & Qt::LeftButton))
updateResizeCursor(e->pos()); updateResizeCursor(e->pos());
} }
void VideoPlayerWindow::mouseReleaseEvent(QMouseEvent* e) {
if (e->button() == Qt::LeftButton) m_resizeMode = ResizeNone;
}
void VideoPlayerWindow::mouseDoubleClickEvent(QMouseEvent* e) { void VideoPlayerWindow::mouseDoubleClickEvent(QMouseEvent* e) {
if (e->button() == Qt::LeftButton) toggleFullscreen(); if (e->button() == Qt::LeftButton) toggleFullscreen();
} }
bool VideoPlayerWindow::eventFilter(QObject* obj, QEvent* ev) { bool VideoPlayerWindow::eventFilter(QObject* obj, QEvent* ev) {
/* Резрез за нижний край: панель контролов перекрывает нижнюю кромку окна, /* Резрез за нижний край: панель контролов перекрывает нижнюю кромку окна,
* поэтому маршрутизируем press из её пустых полей в нативный resize. */ * поэтому маршрутизируем события из её пустых полей в логику resize. */
if (obj == m_controls && !m_fullscreen && ev->type() == QEvent::MouseButtonPress) { if (obj == m_controls && !m_fullscreen) {
QMouseEvent* me = static_cast<QMouseEvent*>(ev); QEvent::Type t = ev->type();
if (me->button() == Qt::LeftButton) { if (t == QEvent::MouseButtonPress || t == QEvent::MouseMove || t == QEvent::MouseButtonRelease) {
QMouseEvent* me = static_cast<QMouseEvent*>(ev);
QPoint winPos = m_controls->mapTo(this, me->pos()); QPoint winPos = m_controls->mapTo(this, me->pos());
ResizeMode m = hitTestResize(winPos); ResizeMode m = hitTestResize(winPos);
if (m != ResizeNone) { if (m == ResizeNone) return QWidget::eventFilter(obj, ev);
m_resizeEdges = edgesForMode(m);
if (windowHandle()) windowHandle()->startSystemResize(m_resizeEdges); QPoint gp = mouseGlobalPos(me);
if (t == QEvent::MouseButtonPress && me->button() == Qt::LeftButton) {
beginResize(m, gp);
return true;
}
if (t == QEvent::MouseMove && m_resizeMode != ResizeNone && (me->buttons() & Qt::LeftButton)) {
applyResize(gp);
return true;
}
if (t == QEvent::MouseButtonRelease && me->button() == Qt::LeftButton) {
m_resizeMode = ResizeNone;
return true; return true;
} }
} }
@ -365,8 +380,6 @@ void VideoPlayerWindow::resizeEvent(QResizeEvent* e) {
m_controls->raise(); m_controls->raise();
m_closeBtn->setGeometry(width() - 40, 8, 32, 32); m_closeBtn->setGeometry(width() - 40, 8, 32, 32);
m_closeBtn->raise(); m_closeBtn->raise();
/* во время нативного resize снэпаем пропорцию после окончания жеста */
if (m_resizeEdges != 0) m_snapTimer->start();
} }
VideoPlayerWindow::ResizeMode VideoPlayerWindow::hitTestResize(const QPoint& pos) const { VideoPlayerWindow::ResizeMode VideoPlayerWindow::hitTestResize(const QPoint& pos) const {
@ -397,33 +410,47 @@ void VideoPlayerWindow::updateResizeCursor(const QPoint& pos) {
} }
} }
Qt::Edges VideoPlayerWindow::edgesForMode(ResizeMode m) { void VideoPlayerWindow::beginResize(ResizeMode m, const QPoint& globalPos) {
switch (m) { m_resizeMode = m;
case ResizeLeft: return Qt::LeftEdge; m_resizeStartGeom = geometry();
case ResizeRight: return Qt::RightEdge; m_resizeStartGlobal = globalPos;
case ResizeTop: return Qt::TopEdge; }
case ResizeBottom: return Qt::BottomEdge;
case ResizeTopLeft: return Qt::TopEdge | Qt::LeftEdge; void VideoPlayerWindow::applyResize(const QPoint& globalPos) {
case ResizeTopRight: return Qt::TopEdge | Qt::RightEdge; const int kMinW = 160;
case ResizeBottomLeft: return Qt::BottomEdge | Qt::LeftEdge; QRect g = m_resizeStartGeom;
case ResizeBottomRight: return Qt::BottomEdge | Qt::RightEdge; double aspect = (g.height() > 0) ? (double)g.width() / g.height() : 1.0;
default: return {}; int minH = qMax(1, qRound(kMinW / aspect));
int dx = globalPos.x() - m_resizeStartGlobal.x();
int dy = globalPos.y() - m_resizeStartGlobal.y();
int newW = g.width(), newH = g.height();
bool widthDriven = true;
switch (m_resizeMode) {
case ResizeRight: case ResizeTopRight: case ResizeBottomRight: newW = g.width() + dx; break;
case ResizeLeft: case ResizeTopLeft: case ResizeBottomLeft: newW = g.width() - dx; break;
case ResizeBottom: newH = g.height() + dy; widthDriven = false; break;
case ResizeTop: newH = g.height() - dy; widthDriven = false; break;
default: return;
} }
} if (widthDriven) newH = qRound(newW / aspect);
else newW = qRound(newH * aspect);
void VideoPlayerWindow::snapToAspect() {
if (m_videoAspect <= 0.0 || m_resizeEdges == 0) return; if (newW < kMinW) { newW = kMinW; newH = minH; }
Qt::Edges edges = m_resizeEdges; if (newH < minH) { newH = minH; newW = kMinW; }
m_resizeEdges = {}; /* сброс до resize(), чтобы снэп не ре-триггерил сам себя */
int w = width(), h = height(); int cx = g.center().x(), cy = g.center().y();
if (w <= 0 || h <= 0) return; int left = g.left(), top = g.top();
switch (m_resizeMode) {
bool verticalOnly = !(edges & (Qt::LeftEdge | Qt::RightEdge)); case ResizeRight: left = g.left(); top = cy - newH / 2; break;
if (verticalOnly) { case ResizeLeft: left = g.right() - newW + 1; top = cy - newH / 2; break;
int newW = qRound(h * m_videoAspect); case ResizeBottom: left = cx - newW / 2; top = g.top(); break;
if (qAbs(newW - w) > 1) resize(newW, h); case ResizeTop: left = cx - newW / 2; top = g.bottom() - newH + 1; break;
} else { case ResizeTopLeft: left = g.right() - newW + 1; top = g.bottom() - newH + 1; break;
int newH = qRound(w / m_videoAspect); case ResizeTopRight: left = g.left(); top = g.bottom() - newH + 1; break;
if (qAbs(newH - h) > 1) resize(w, newH); case ResizeBottomLeft: left = g.right() - newW + 1; top = g.top(); break;
case ResizeBottomRight: left = g.left(); top = g.top(); break;
default: return;
} }
setGeometry(left, top, newW, newH);
} }

11
tools/chatgui/src/videoplayer_window.h

@ -43,6 +43,7 @@ protected:
void keyPressEvent(QKeyEvent* event) override; void keyPressEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override; void mouseDoubleClickEvent(QMouseEvent* event) override;
void closeEvent(QCloseEvent* event) override; void closeEvent(QCloseEvent* event) override;
void resizeEvent(QResizeEvent* event) override; void resizeEvent(QResizeEvent* event) override;
@ -74,8 +75,8 @@ private:
QSize fitWindowSize(int vw, int vh) const; QSize fitWindowSize(int vw, int vh) const;
ResizeMode hitTestResize(const QPoint& localPos) const; ResizeMode hitTestResize(const QPoint& localPos) const;
void updateResizeCursor(const QPoint& localPos); void updateResizeCursor(const QPoint& localPos);
static Qt::Edges edgesForMode(ResizeMode m); void applyResize(const QPoint& globalPos);
void snapToAspect(); void beginResize(ResizeMode m, const QPoint& globalPos);
VideoRenderWidget* m_render = nullptr; VideoRenderWidget* m_render = nullptr;
QWidget* m_controls = nullptr; QWidget* m_controls = nullptr;
@ -87,8 +88,8 @@ private:
bool m_fullscreen = false; bool m_fullscreen = false;
bool m_sliderDragging = false; bool m_sliderDragging = false;
double m_videoAspect = 0.0; ResizeMode m_resizeMode = ResizeNone;
Qt::Edges m_resizeEdges = {}; QRect m_resizeStartGeom;
QPoint m_resizeStartGlobal;
QTimer* m_hideTimer = nullptr; QTimer* m_hideTimer = nullptr;
QTimer* m_snapTimer = nullptr;
}; };

Loading…
Cancel
Save