chatgui: плеер — фикс drag, центр/fullscreen при открытии, resize за края с сохранением пропорций
- m_render WA_TransparentForMouseEvents — события мыши напрямую в окно (drag работает) - открытие: видео влезает — натуральный размер по центру; больше экрана — fullscreen - resize мышью за 4 края и 4 угла, пропорции сохраняются (якорь = противоположный край) - нижний край доступен через eventFilter на панели контролов (пустые поля пропагируют) - сброс fullscreen при повторном playFile
This commit is contained in:
1 parent
76182d018f
commit
d09a6af52a
7 files changed
+513
-17
No files matched your search
@@ -70,9 +70,11 @@ VideoPlayerWindow::VideoPlayerWindow(QWidget* parent)
|
||||
m_engine = new VideoPlayerEngine(this);
|
||||
|
||||
m_render = new VideoRenderWidget(this);
|
||||
m_render->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
|
||||
m_controls = new QWidget(this);
|
||||
m_controls->setObjectName("videoControls");
|
||||
m_controls->installEventFilter(this);
|
||||
m_controls->setStyleSheet(
|
||||
"#videoControls { background-color: rgba(0,0,0,0.65); }"
|
||||
"QPushButton { background: transparent; color: white; border: none; font-size: 16px; }"
|
||||
@@ -91,7 +93,7 @@ VideoPlayerWindow::VideoPlayerWindow(QWidget* parent)
|
||||
m_timeLabel = new QLabel(QStringLiteral("0:00 / 0:00"), m_controls);
|
||||
|
||||
auto* lay = new QHBoxLayout(m_controls);
|
||||
lay->setContentsMargins(8, 4, 8, 4);
|
||||
lay->setContentsMargins(8, 3, 8, 6);
|
||||
lay->setSpacing(6);
|
||||
lay->addWidget(m_playBtn);
|
||||
lay->addWidget(m_slider, 1);
|
||||
@@ -123,6 +125,7 @@ void VideoPlayerWindow::playFile(const QString& path) {
|
||||
m_render->clearFrame();
|
||||
m_slider->setRange(0, 0);
|
||||
m_timeLabel->setText(QStringLiteral("0:00 / 0:00"));
|
||||
if (m_fullscreen) { showNormal(); m_fullscreen = false; setControlsVisible(true); unsetCursor(); }
|
||||
show();
|
||||
raise();
|
||||
activateWindow();
|
||||
@@ -133,16 +136,25 @@ void VideoPlayerWindow::onEngineOpened(int width, int height, qint64 durationMs,
|
||||
Q_UNUSED(hasAudio);
|
||||
m_slider->setRange(0, (int)durationMs);
|
||||
updateTimeLabels(0);
|
||||
|
||||
QScreen* scr = QApplication::primaryScreen();
|
||||
QRect ag = scr ? scr->availableGeometry() : QRect(0, 0, 1280, 720);
|
||||
QSize s = fitWindowSize(width, height);
|
||||
resize(s);
|
||||
if (!m_fullscreen) {
|
||||
QScreen* scr = QApplication::primaryScreen();
|
||||
if (scr) {
|
||||
QRect ag = scr->availableGeometry();
|
||||
move(ag.center().x() - s.width() / 2, ag.center().y() - s.height() / 2);
|
||||
}
|
||||
|
||||
if (width <= ag.width() && height <= ag.height()) {
|
||||
/* видео помещается — натуральный размер по центру */
|
||||
resize(s);
|
||||
move(ag.center().x() - s.width() / 2, ag.center().y() - s.height() / 2);
|
||||
} else {
|
||||
/* видео больше экрана — fullscreen (нормальная геометрия для выхода уже задана) */
|
||||
resize(s);
|
||||
move(ag.center().x() - s.width() / 2, ag.center().y() - s.height() / 2);
|
||||
showFullScreen();
|
||||
m_fullscreen = true;
|
||||
m_hideTimer->start();
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "VideoWindow: %dx%d dur=%lldms", width, height, (long long)durationMs);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "VideoWindow: %dx%d dur=%lldms size=%dx%d fullscreen=%d",
|
||||
width, height, (long long)durationMs, s.width(), s.height(), m_fullscreen ? 1 : 0);
|
||||
}
|
||||
|
||||
void VideoPlayerWindow::onPositionChanged(qint64 ms) {
|
||||
@@ -239,9 +251,10 @@ QSize VideoPlayerWindow::fitWindowSize(int vw, int vh) const {
|
||||
if (vw <= 0 || vh <= 0) return QSize(640, 360);
|
||||
QScreen* scr = QApplication::primaryScreen();
|
||||
QSize avail(1280, 720);
|
||||
if (scr) avail = scr->availableGeometry().size() * 0.85;
|
||||
if (scr) avail = scr->availableGeometry().size();
|
||||
QSize s(vw, vh);
|
||||
s.scale(avail, Qt::KeepAspectRatio);
|
||||
if (s.width() > avail.width() || s.height() > avail.height())
|
||||
s.scale(avail, Qt::KeepAspectRatio);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -268,6 +281,11 @@ void VideoPlayerWindow::mousePressEvent(QMouseEvent* e) {
|
||||
/* клик в fullscreen — показать/скрыть контролы */
|
||||
if (m_controls->isVisible()) hideControls();
|
||||
else showControls();
|
||||
return;
|
||||
}
|
||||
ResizeMode m = hitTestResize(e->pos());
|
||||
if (m != ResizeNone) {
|
||||
beginResize(m, mouseGlobalPos(e));
|
||||
} else {
|
||||
m_dragging = true;
|
||||
m_dragStart = mouseGlobalPos(e);
|
||||
@@ -281,19 +299,58 @@ void VideoPlayerWindow::mouseMoveEvent(QMouseEvent* e) {
|
||||
showControls();
|
||||
return;
|
||||
}
|
||||
if (m_resizeMode != ResizeNone && (e->buttons() & Qt::LeftButton)) {
|
||||
applyResize(mouseGlobalPos(e));
|
||||
return;
|
||||
}
|
||||
if (m_dragging && (e->buttons() & Qt::LeftButton)) {
|
||||
move(m_windowStart + (mouseGlobalPos(e) - m_dragStart));
|
||||
return;
|
||||
}
|
||||
if (!(e->buttons() & Qt::LeftButton))
|
||||
updateResizeCursor(e->pos());
|
||||
}
|
||||
|
||||
void VideoPlayerWindow::mouseReleaseEvent(QMouseEvent* e) {
|
||||
if (e->button() == Qt::LeftButton) m_dragging = false;
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
m_dragging = false;
|
||||
m_resizeMode = ResizeNone;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoPlayerWindow::mouseDoubleClickEvent(QMouseEvent* e) {
|
||||
if (e->button() == Qt::LeftButton) toggleFullscreen();
|
||||
}
|
||||
|
||||
bool VideoPlayerWindow::eventFilter(QObject* obj, QEvent* ev) {
|
||||
/* Резрез за нижний край: панель контролов перекрывает нижнюю кромку окна,
|
||||
* поэтому маршрутизируем события из её пустых полей в логику 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) 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QWidget::eventFilter(obj, ev);
|
||||
}
|
||||
|
||||
void VideoPlayerWindow::closeEvent(QCloseEvent* e) {
|
||||
m_engine->close();
|
||||
emit closed();
|
||||
@@ -307,3 +364,76 @@ void VideoPlayerWindow::resizeEvent(QResizeEvent* e) {
|
||||
m_controls->setGeometry(0, height() - ch, width(), ch);
|
||||
m_controls->raise();
|
||||
}
|
||||
|
||||
VideoPlayerWindow::ResizeMode VideoPlayerWindow::hitTestResize(const QPoint& pos) const {
|
||||
const int M = 6;
|
||||
int w = width(), h = height();
|
||||
bool l = pos.x() <= M;
|
||||
bool r = pos.x() >= w - M - 1;
|
||||
bool t = pos.y() <= M;
|
||||
bool b = pos.y() >= h - M - 1;
|
||||
if (t && l) return ResizeTopLeft;
|
||||
if (t && r) return ResizeTopRight;
|
||||
if (b && l) return ResizeBottomLeft;
|
||||
if (b && r) return ResizeBottomRight;
|
||||
if (l) return ResizeLeft;
|
||||
if (r) return ResizeRight;
|
||||
if (t) return ResizeTop;
|
||||
if (b) return ResizeBottom;
|
||||
return ResizeNone;
|
||||
}
|
||||
|
||||
void VideoPlayerWindow::updateResizeCursor(const QPoint& pos) {
|
||||
switch (hitTestResize(pos)) {
|
||||
case ResizeTopLeft: case ResizeBottomRight: setCursor(Qt::SizeFDiagCursor); break;
|
||||
case ResizeTopRight: case ResizeBottomLeft: setCursor(Qt::SizeBDiagCursor); break;
|
||||
case ResizeLeft: case ResizeRight: setCursor(Qt::SizeHorCursor); break;
|
||||
case ResizeTop: case ResizeBottom: setCursor(Qt::SizeVerCursor); break;
|
||||
default: unsetCursor(); break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
Reference in new issue
Block a user