chatgui: вернуть ручной resize с пропорцией (двигает окно, край под мышью) + фикс перемотки
- resize: ручной setGeometry с живой пропорцией (якорь = противоположный край); drag — нативный startSystemMove - перемотка: QProxyStyle SH_Slider_AbsoluteSetButtons=LeftButton вместо сломанного SeekSlider (sliderReleased не эмитился — seekMs не вызывался)
This commit is contained in:
1 parent
645591bb64
commit
3adf9871fb
2 files changed
+92
-64
No files matched your search
@@ -15,6 +15,7 @@
|
||||
#include <QApplication>
|
||||
#include <QScreen>
|
||||
#include <QStyle>
|
||||
#include <QProxyStyle>
|
||||
#include <QWindow>
|
||||
|
||||
/* ─── 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 {
|
||||
public:
|
||||
using QSlider::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
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
/* Слайдер перемотки: клик по любому месту шкалы = прыжок к позиции (нативно через стиль). */
|
||||
class SeekSliderStyle : public QProxyStyle {
|
||||
public:
|
||||
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<QMouseEvent*>(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<QMouseEvent*>(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::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;
|
||||
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();
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
Reference in new issue
Block a user