@ -15,6 +15,7 @@
# include <QApplication>
# include <QScreen>
# include <QStyle>
# include <QWindow>
/* ─── VideoRenderWidget ─── */
@ -51,13 +52,25 @@ static QString fmtTime(qint64 ms) {
return QString ( " %1:%2 " ) . arg ( s / 60 ) . arg ( s % 60 , 2 , 10 , QChar ( ' 0 ' ) ) ;
}
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 SeekSlider : public QSlider {
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 ) ;
}
}
} ;
VideoPlayerWindow : : VideoPlayerWindow ( QWidget * parent )
: QWidget ( parent , Qt : : Window | Qt : : FramelessWindowHint )
@ -74,6 +87,7 @@ VideoPlayerWindow::VideoPlayerWindow(QWidget* parent)
m_controls = new QWidget ( this ) ;
m_controls - > setObjectName ( " videoControls " ) ;
m_controls - > setCursor ( Qt : : ArrowCursor ) ;
m_controls - > installEventFilter ( this ) ;
m_controls - > setStyleSheet (
" #videoControls { background-color: rgba(0,0,0,0.65); } "
@ -86,9 +100,7 @@ VideoPlayerWindow::VideoPlayerWindow(QWidget* parent)
m_playBtn = new QPushButton ( QStringLiteral ( " ⏸ " ) , m_controls ) ;
m_playBtn - > setToolTip ( QStringLiteral ( " Пауза " ) ) ;
m_closeBtn = new QPushButton ( QStringLiteral ( " ✕ " ) , m_controls ) ;
m_closeBtn - > setToolTip ( QStringLiteral ( " Закрыть " ) ) ;
m_slider = new QSlider ( Qt : : Horizontal , m_controls ) ;
m_slider = new SeekSlider ( Qt : : Horizontal , m_controls ) ;
m_slider - > setRange ( 0 , 0 ) ;
m_timeLabel = new QLabel ( QStringLiteral ( " 0:00 / 0:00 " ) , m_controls ) ;
@ -98,13 +110,26 @@ VideoPlayerWindow::VideoPlayerWindow(QWidget* parent)
lay - > addWidget ( m_playBtn ) ;
lay - > addWidget ( m_slider , 1 ) ;
lay - > addWidget ( m_timeLabel ) ;
lay - > addWidget ( m_closeBtn ) ;
/* крестик закрытия — отдельно, справа вверху */
m_closeBtn = new QPushButton ( QStringLiteral ( " ✕ " ) , this ) ;
m_closeBtn - > setToolTip ( QStringLiteral ( " Закрыть " ) ) ;
m_closeBtn - > setCursor ( Qt : : ArrowCursor ) ;
m_closeBtn - > setStyleSheet (
" QPushButton { background: rgba(0,0,0,0.5); color: white; border: none; "
" border-radius: 4px; font-size: 14px; } "
" QPushButton:hover { background: rgba(255,255,255,0.25); } " ) ;
m_hideTimer = new QTimer ( this ) ;
m_hideTimer - > setSingleShot ( true ) ;
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 ) ;
@ -136,6 +161,7 @@ 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 ) ;
@ -226,6 +252,7 @@ void VideoPlayerWindow::toggleFullscreen() {
void VideoPlayerWindow : : setControlsVisible ( bool visible ) {
m_controls - > setVisible ( visible ) ;
m_closeBtn - > setVisible ( visible ) ;
}
void VideoPlayerWindow : : showControls ( ) {
@ -285,11 +312,10 @@ void VideoPlayerWindow::mousePressEvent(QMouseEvent* e) {
}
ResizeMode m = hitTestResize ( e - > pos ( ) ) ;
if ( m ! = ResizeNone ) {
beginResize ( m , mouseGlobalPos ( e ) ) ;
m_resizeEdges = edgesForMode ( m ) ;
if ( windowHandle ( ) ) windowHandle ( ) - > startSystemResize ( m_resizeEdges ) ;
} else {
m_dragging = true ;
m_dragStart = mouseGlobalPos ( e ) ;
m_windowStart = frameGeometry ( ) . topLeft ( ) ;
if ( windowHandle ( ) ) windowHandle ( ) - > startSystemMove ( ) ;
}
}
}
@ -299,51 +325,25 @@ 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 ;
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 ) {
* п о э т о м у м а р ш р у т и з и р у е м press и з е ё п у с т ы х п о л е й в н а т и в н ы й resize . */
if ( obj = = m_controls & & ! m_fullscreen & & ev - > type ( ) = = QEvent : : MouseButtonPress ) {
QMouseEvent * me = static_cast < QMouseEvent * > ( ev ) ;
if ( me - > button ( ) = = Qt : : LeftButton ) {
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 ;
if ( m ! = ResizeNone ) {
m_resizeEdges = edgesForMode ( m ) ;
if ( windowHandle ( ) ) windowHandle ( ) - > startSystemResize ( m_resizeEdges ) ;
return true ;
}
}
@ -363,6 +363,10 @@ void VideoPlayerWindow::resizeEvent(QResizeEvent* e) {
int ch = 48 ;
m_controls - > setGeometry ( 0 , height ( ) - ch , width ( ) , ch ) ;
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 {
@ -393,47 +397,33 @@ void VideoPlayerWindow::updateResizeCursor(const QPoint& pos) {
}
}
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 ;
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 { } ;
}
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 ;
}
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 ) ;
}
setGeometry ( left , top , newW , newH ) ;
}