From 3adf9871fb76ef3fef84531b1d4938b63fa4f2f7 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 8 Sep 2026 02:39:06 +0300 Subject: [PATCH] =?UTF-8?q?chatgui:=20=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D1=82?= =?UTF-8?q?=D1=8C=20=D1=80=D1=83=D1=87=D0=BD=D0=BE=D0=B9=20resize=20=D1=81?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=BF=D0=BE=D1=80=D1=86=D0=B8=D0=B5=D0=B9?= =?UTF-8?q?=20(=D0=B4=D0=B2=D0=B8=D0=B3=D0=B0=D0=B5=D1=82=20=D0=BE=D0=BA?= =?UTF-8?q?=D0=BD=D0=BE,=20=D0=BA=D1=80=D0=B0=D0=B9=20=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=20=D0=BC=D1=8B=D1=88=D1=8C=D1=8E)=20+=20=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D1=81=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=BE=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - resize: ручной setGeometry с живой пропорцией (якорь = противоположный край); drag — нативный startSystemMove - перемотка: QProxyStyle SH_Slider_AbsoluteSetButtons=LeftButton вместо сломанного SeekSlider (sliderReleased не эмитился — seekMs не вызывался) --- tools/chatgui/src/videoplayer_window.cpp | 149 +++++++++++++---------- tools/chatgui/src/videoplayer_window.h | 11 +- 2 files changed, 94 insertions(+), 66 deletions(-) diff --git a/tools/chatgui/src/videoplayer_window.cpp b/tools/chatgui/src/videoplayer_window.cpp index c5d453a0..c28e33f3 100644 --- a/tools/chatgui/src/videoplayer_window.cpp +++ b/tools/chatgui/src/videoplayer_window.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include /* ─── VideoRenderWidget ─── */ @@ -52,23 +53,24 @@ static QString fmtTime(qint64 ms) { return QString("%1:%2").arg(s / 60).arg(s % 60, 2, 10, QChar('0')); } -/* Слайдер перемотки: клик по любому месту шкалы = прыжок к позиции (драг кружка сохраняется). */ -class SeekSlider : public QSlider { +static QPoint mouseGlobalPos(const QMouseEvent* e) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + return e->globalPosition().toPoint(); +#else + return e->globalPos(); +#endif +} + +/* Слайдер перемотки: клик по любому месту шкалы = прыжок к позиции (нативно через стиль). */ +class SeekSliderStyle : public QProxyStyle { public: - using QSlider::QSlider; - -protected: - void mousePressEvent(QMouseEvent* e) override { - if (e->button() == Qt::LeftButton) { - setSliderDown(true); - int v = QStyle::sliderValueFromPosition(minimum(), maximum(), - (int)e->position().x(), width()); - setValue(v); - emit sliderPressed(); - e->accept(); - } else { - QSlider::mousePressEvent(e); - } + using QProxyStyle::QProxyStyle; + + int styleHint(StyleHint hint, const QStyleOption* opt, const QWidget* w, + QStyleHintReturn* ret) const override { + if (hint == QStyle::SH_Slider_AbsoluteSetButtons) + return (int)Qt::LeftButton; + return QProxyStyle::styleHint(hint, opt, w, ret); } }; @@ -100,7 +102,8 @@ VideoPlayerWindow::VideoPlayerWindow(QWidget* parent) m_playBtn = new QPushButton(QStringLiteral("⏸"), m_controls); 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_timeLabel = new QLabel(QStringLiteral("0:00 / 0:00"), m_controls); @@ -125,11 +128,6 @@ VideoPlayerWindow::VideoPlayerWindow(QWidget* parent) m_hideTimer->setInterval(2500); 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_closeBtn, &QPushButton::clicked, this, &VideoPlayerWindow::onClose); connect(m_slider, &QSlider::sliderPressed, this, &VideoPlayerWindow::onSliderPressed); @@ -161,7 +159,6 @@ void VideoPlayerWindow::onEngineOpened(int width, int height, qint64 durationMs, Q_UNUSED(hasAudio); m_slider->setRange(0, (int)durationMs); updateTimeLabels(0); - m_videoAspect = (height > 0) ? (double)width / height : 0.0; QScreen* scr = QApplication::primaryScreen(); QRect ag = scr ? scr->availableGeometry() : QRect(0, 0, 1280, 720); @@ -312,8 +309,7 @@ void VideoPlayerWindow::mousePressEvent(QMouseEvent* e) { } ResizeMode m = hitTestResize(e->pos()); if (m != ResizeNone) { - m_resizeEdges = edgesForMode(m); - if (windowHandle()) windowHandle()->startSystemResize(m_resizeEdges); + beginResize(m, mouseGlobalPos(e)); } else { if (windowHandle()) windowHandle()->startSystemMove(); } @@ -325,25 +321,44 @@ void VideoPlayerWindow::mouseMoveEvent(QMouseEvent* e) { showControls(); return; } + if (m_resizeMode != ResizeNone && (e->buttons() & Qt::LeftButton)) { + applyResize(mouseGlobalPos(e)); + return; + } if (!(e->buttons() & Qt::LeftButton)) updateResizeCursor(e->pos()); } +void VideoPlayerWindow::mouseReleaseEvent(QMouseEvent* e) { + if (e->button() == Qt::LeftButton) m_resizeMode = ResizeNone; +} + void VideoPlayerWindow::mouseDoubleClickEvent(QMouseEvent* e) { if (e->button() == Qt::LeftButton) toggleFullscreen(); } bool VideoPlayerWindow::eventFilter(QObject* obj, QEvent* ev) { /* Резрез за нижний край: панель контролов перекрывает нижнюю кромку окна, - * поэтому маршрутизируем press из её пустых полей в нативный resize. */ - if (obj == m_controls && !m_fullscreen && ev->type() == QEvent::MouseButtonPress) { - QMouseEvent* me = static_cast(ev); - if (me->button() == Qt::LeftButton) { + * поэтому маршрутизируем события из её пустых полей в логику resize. */ + if (obj == m_controls && !m_fullscreen) { + QEvent::Type t = ev->type(); + if (t == QEvent::MouseButtonPress || t == QEvent::MouseMove || t == QEvent::MouseButtonRelease) { + QMouseEvent* me = static_cast(ev); QPoint winPos = m_controls->mapTo(this, me->pos()); ResizeMode m = hitTestResize(winPos); - if (m != ResizeNone) { - m_resizeEdges = edgesForMode(m); - if (windowHandle()) windowHandle()->startSystemResize(m_resizeEdges); + if (m == ResizeNone) return QWidget::eventFilter(obj, ev); + + 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; } } @@ -365,8 +380,6 @@ void VideoPlayerWindow::resizeEvent(QResizeEvent* e) { m_controls->raise(); m_closeBtn->setGeometry(width() - 40, 8, 32, 32); m_closeBtn->raise(); - /* во время нативного resize снэпаем пропорцию после окончания жеста */ - if (m_resizeEdges != 0) m_snapTimer->start(); } VideoPlayerWindow::ResizeMode VideoPlayerWindow::hitTestResize(const QPoint& pos) const { @@ -397,33 +410,47 @@ void VideoPlayerWindow::updateResizeCursor(const QPoint& pos) { } } -Qt::Edges VideoPlayerWindow::edgesForMode(ResizeMode m) { - switch (m) { - case ResizeLeft: return Qt::LeftEdge; - case ResizeRight: return Qt::RightEdge; - case ResizeTop: return Qt::TopEdge; - case ResizeBottom: return Qt::BottomEdge; - case ResizeTopLeft: return Qt::TopEdge | Qt::LeftEdge; - case ResizeTopRight: return Qt::TopEdge | Qt::RightEdge; - case ResizeBottomLeft: return Qt::BottomEdge | Qt::LeftEdge; - case ResizeBottomRight: return Qt::BottomEdge | Qt::RightEdge; - default: return {}; +void VideoPlayerWindow::beginResize(ResizeMode m, const QPoint& globalPos) { + m_resizeMode = m; + m_resizeStartGeom = geometry(); + m_resizeStartGlobal = globalPos; +} + +void VideoPlayerWindow::applyResize(const QPoint& globalPos) { + const int kMinW = 160; + QRect g = m_resizeStartGeom; + double aspect = (g.height() > 0) ? (double)g.width() / g.height() : 1.0; + 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; } -} - -void VideoPlayerWindow::snapToAspect() { - if (m_videoAspect <= 0.0 || m_resizeEdges == 0) return; - Qt::Edges edges = m_resizeEdges; - m_resizeEdges = {}; /* сброс до resize(), чтобы снэп не ре-триггерил сам себя */ - int w = width(), h = height(); - if (w <= 0 || h <= 0) return; - - bool verticalOnly = !(edges & (Qt::LeftEdge | Qt::RightEdge)); - if (verticalOnly) { - int newW = qRound(h * m_videoAspect); - if (qAbs(newW - w) > 1) resize(newW, h); - } else { - int newH = qRound(w / m_videoAspect); - if (qAbs(newH - h) > 1) resize(w, newH); + if (widthDriven) newH = qRound(newW / aspect); + else newW = qRound(newH * aspect); + + if (newW < kMinW) { newW = kMinW; newH = minH; } + if (newH < minH) { newH = minH; newW = kMinW; } + + int cx = g.center().x(), cy = g.center().y(); + int left = g.left(), top = g.top(); + switch (m_resizeMode) { + case ResizeRight: left = g.left(); top = cy - newH / 2; break; + case ResizeLeft: left = g.right() - newW + 1; top = cy - newH / 2; break; + case ResizeBottom: left = cx - newW / 2; top = g.top(); break; + case ResizeTop: left = cx - newW / 2; top = g.bottom() - newH + 1; break; + case ResizeTopLeft: left = g.right() - newW + 1; top = g.bottom() - newH + 1; break; + case ResizeTopRight: left = g.left(); top = g.bottom() - newH + 1; break; + 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); } diff --git a/tools/chatgui/src/videoplayer_window.h b/tools/chatgui/src/videoplayer_window.h index 58235a84..b2c05e5c 100644 --- a/tools/chatgui/src/videoplayer_window.h +++ b/tools/chatgui/src/videoplayer_window.h @@ -43,6 +43,7 @@ protected: void keyPressEvent(QKeyEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; void mouseDoubleClickEvent(QMouseEvent* event) override; void closeEvent(QCloseEvent* event) override; void resizeEvent(QResizeEvent* event) override; @@ -74,8 +75,8 @@ private: QSize fitWindowSize(int vw, int vh) const; ResizeMode hitTestResize(const QPoint& localPos) const; void updateResizeCursor(const QPoint& localPos); - static Qt::Edges edgesForMode(ResizeMode m); - void snapToAspect(); + void applyResize(const QPoint& globalPos); + void beginResize(ResizeMode m, const QPoint& globalPos); VideoRenderWidget* m_render = nullptr; QWidget* m_controls = nullptr; @@ -87,8 +88,8 @@ private: bool m_fullscreen = false; bool m_sliderDragging = false; - double m_videoAspect = 0.0; - Qt::Edges m_resizeEdges = {}; + ResizeMode m_resizeMode = ResizeNone; + QRect m_resizeStartGeom; + QPoint m_resizeStartGlobal; QTimer* m_hideTimer = nullptr; - QTimer* m_snapTimer = nullptr; };