[vlc-commits] qml: add support for client side decoration
Pierre Lamot
git at videolan.org
Thu Oct 8 09:42:03 CEST 2020
vlc | branch: master | Pierre Lamot <pierre at videolabs.io> | Fri Sep 25 18:30:07 2020 +0200| [e3ead9cbdd107b9a26f4a2f37d7675d6a16195bc] | committer: Pierre Lamot
qml: add support for client side decoration
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=e3ead9cbdd107b9a26f4a2f37d7675d6a16195bc
---
modules/gui/qt/Makefile.am | 3 +
modules/gui/qt/maininterface/compositor_dcomp.cpp | 8 ++-
modules/gui/qt/maininterface/compositor_dummy.cpp | 10 +++-
modules/gui/qt/maininterface/compositor_win7.cpp | 6 +-
.../qt/maininterface/interface_window_handler.cpp | 67 ++++++++++++++++++++++
.../qt/maininterface/interface_window_handler.hpp | 8 +++
modules/gui/qt/maininterface/main_interface.cpp | 12 ++++
modules/gui/qt/maininterface/main_interface.hpp | 10 +++-
.../gui/qt/maininterface/main_interface_win32.cpp | 13 +++++
modules/gui/qt/maininterface/mainui.cpp | 6 +-
modules/gui/qt/maininterface/mainui.hpp | 3 +-
modules/gui/qt/maininterface/qml/BannerSources.qml | 19 ++++++
modules/gui/qt/player/qml/ResumeDialog.qml | 21 ++++++-
modules/gui/qt/player/qml/TopBar.qml | 20 +++++++
modules/gui/qt/qt.cpp | 14 +++++
modules/gui/qt/style/VLCColors.qml | 3 +
modules/gui/qt/style/VLCStyle.qml | 4 +-
modules/gui/qt/vlc.qrc | 3 +
.../qt/widgets/qml/CSDTitlebarTapNDrapHandler.qml | 46 +++++++++++++++
modules/gui/qt/widgets/qml/CSDWindowButton.qml | 57 ++++++++++++++++++
modules/gui/qt/widgets/qml/CSDWindowButtonSet.qml | 62 ++++++++++++++++++++
21 files changed, 385 insertions(+), 10 deletions(-)
diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 3fae174717..c85f52fb83 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -686,6 +686,9 @@ libqt_plugin_la_QML = \
gui/qt/widgets/qml/CaptionLabel.qml \
gui/qt/widgets/qml/ComboBoxExt.qml \
gui/qt/widgets/qml/ContextButton.qml \
+ gui/qt/widgets/qml/CSDWindowButton.qml \
+ gui/qt/widgets/qml/CSDWindowButtonSet.qml \
+ gui/qt/widgets/qml/CSDTitlebarTapNDrapHandler.qml \
gui/qt/widgets/qml/DNDLabel.qml \
gui/qt/widgets/qml/DrawerExt.qml \
gui/qt/widgets/qml/ExpandGridView.qml \
diff --git a/modules/gui/qt/maininterface/compositor_dcomp.cpp b/modules/gui/qt/maininterface/compositor_dcomp.cpp
index f459480447..361746027d 100644
--- a/modules/gui/qt/maininterface/compositor_dcomp.cpp
+++ b/modules/gui/qt/maininterface/compositor_dcomp.cpp
@@ -198,6 +198,7 @@ MainInterface* CompositorDirectComposition::makeMainInterface()
m_rootWindow->setAttribute(Qt::WA_NativeWindow);
m_rootWindow->setAttribute(Qt::WA_DontCreateNativeAncestors);
m_rootWindow->setAttribute(Qt::WA_TranslucentBackground);
+
m_rootWindow->winId();
m_rootWindow->show();
@@ -234,7 +235,12 @@ MainInterface* CompositorDirectComposition::makeMainInterface()
connect(m_qmlVideoSurfaceProvider.get(), &VideoSurfaceProvider::hasVideoChanged,
m_interfaceWindowHandler, &InterfaceWindowHandlerWin32::onVideoEmbedChanged);
- m_ui = std::make_unique<MainUI>(m_intf, m_rootWindow);
+ connect(m_rootWindow, &MainInterface::requestInterfaceMaximized,
+ m_rootWindow, &MainInterface::showMaximized);
+ connect(m_rootWindow, &MainInterface::requestInterfaceNormal,
+ m_rootWindow, &MainInterface::showNormal);
+
+ m_ui = std::make_unique<MainUI>(m_intf, m_rootWindow, m_rootWindow->windowHandle());
ret = m_ui->setup(m_uiSurface->engine());
if (! ret)
{
diff --git a/modules/gui/qt/maininterface/compositor_dummy.cpp b/modules/gui/qt/maininterface/compositor_dummy.cpp
index 81e5d3f867..f972ac4790 100644
--- a/modules/gui/qt/maininterface/compositor_dummy.cpp
+++ b/modules/gui/qt/maininterface/compositor_dummy.cpp
@@ -32,17 +32,25 @@ CompositorDummy::CompositorDummy(intf_thread_t *p_intf, QObject* parent)
MainInterface* CompositorDummy::makeMainInterface()
{
m_rootWindow = new MainInterface(m_intf);
+ if (m_rootWindow->useClientSideDecoration())
+ m_rootWindow->setWindowFlag(Qt::FramelessWindowHint);
m_rootWindow->show();
QQuickWidget* centralWidget = new QQuickWidget(m_rootWindow);
centralWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
new InterfaceWindowHandler(m_intf, m_rootWindow, m_rootWindow->windowHandle(), m_rootWindow);
- MainUI* m_ui = new MainUI(m_intf, m_rootWindow, this);
+ MainUI* m_ui = new MainUI(m_intf, m_rootWindow, m_rootWindow->windowHandle(), this);
m_ui->setup(centralWidget->engine());
centralWidget->setContent(QUrl(), m_ui->getComponent(), m_ui->createRootItem());
m_rootWindow->setCentralWidget(centralWidget);
+
+ connect(m_rootWindow, &MainInterface::requestInterfaceMaximized,
+ m_rootWindow, &MainInterface::showMaximized);
+ connect(m_rootWindow, &MainInterface::requestInterfaceNormal,
+ m_rootWindow, &MainInterface::showNormal);
+
return m_rootWindow;
}
diff --git a/modules/gui/qt/maininterface/compositor_win7.cpp b/modules/gui/qt/maininterface/compositor_win7.cpp
index dab17a6997..55d6f54a1b 100644
--- a/modules/gui/qt/maininterface/compositor_win7.cpp
+++ b/modules/gui/qt/maininterface/compositor_win7.cpp
@@ -198,7 +198,7 @@ MainInterface* CompositorWin7::makeMainInterface()
m_taskbarWidget = new WinTaskbarWidget(m_intf, m_qmlView.get(), this);
qApp->installNativeEventFilter(m_taskbarWidget);
- MainUI* m_ui = new MainUI(m_intf, m_rootWindow, this);
+ MainUI* m_ui = new MainUI(m_intf, m_rootWindow, m_qmlView.get(), this);
m_ui->setup(m_qmlView->engine());
@@ -208,6 +208,10 @@ MainInterface* CompositorWin7::makeMainInterface()
m_qmlView.get(), &QQuickView::setTitle);
connect(m_rootWindow, &MainInterface::windowIconChanged,
m_qmlView.get(), &QQuickView::setIcon);
+ connect(m_rootWindow, &MainInterface::requestInterfaceMaximized,
+ m_qmlView.get(), &QWindow::showMaximized);
+ connect(m_rootWindow, &MainInterface::requestInterfaceNormal,
+ m_qmlView.get(), &QWindow::showNormal);
return m_rootWindow;
}
diff --git a/modules/gui/qt/maininterface/interface_window_handler.cpp b/modules/gui/qt/maininterface/interface_window_handler.cpp
index 084d0b3a45..c1a2ffc3f5 100644
--- a/modules/gui/qt/maininterface/interface_window_handler.cpp
+++ b/modules/gui/qt/maininterface/interface_window_handler.cpp
@@ -85,6 +85,60 @@ InterfaceWindowHandler::~InterfaceWindowHandler()
WindowStateHolder::holdFullscreen( m_window, WindowStateHolder::INTERFACE, false );
}
+#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
+bool InterfaceWindowHandler::CSDSetCursor(QMouseEvent* mouseEvent)
+{
+ if (!m_mainInterface->useClientSideDecoration())
+ return false;
+ if ((m_window->visibility() & QWindow::Maximized) != 0)
+ return false;
+ Qt::CursorShape shape;
+ const int x = mouseEvent->x();
+ const int y = mouseEvent->y();
+ const int winHeight = m_window->height();
+ const int winWidth = m_window->width();
+ const int b = 5 * m_mainInterface->getIntfScaleFactor();
+
+ if (x < b && y < b) shape = Qt::SizeFDiagCursor;
+ else if (x >= winWidth - b && y >= winHeight - b) shape = Qt::SizeFDiagCursor;
+ else if (x >= winWidth - b && y < b) shape = Qt::SizeBDiagCursor;
+ else if (x < b && y >= winHeight - b) shape = Qt::SizeBDiagCursor;
+ else if (x < b || x >= winWidth - b) shape = Qt::SizeHorCursor;
+ else if (y < b || y >= winHeight - b) shape = Qt::SizeVerCursor;
+ else if (m_hasResizeCursor) {
+ m_window->unsetCursor();
+ m_hasResizeCursor = false;
+ return false;
+ } else {
+ return false;
+ }
+ m_hasResizeCursor = true;
+ m_window->setCursor(shape);
+ return false;
+}
+
+bool InterfaceWindowHandler::CSDHandleClick(QMouseEvent* mouseEvent)
+{
+ if (!m_mainInterface->useClientSideDecoration())
+ return false;
+ const int b = 5 * m_mainInterface->getIntfScaleFactor();
+ if( mouseEvent->buttons() != Qt::LeftButton)
+ return false;
+ if ((m_window->visibility() & QWindow::Maximized) != 0)
+ return false;
+ Qt::Edges edge;
+ if (mouseEvent->x() < b) { edge |= Qt::LeftEdge; }
+ if (mouseEvent->x() > m_window->width() - b) { edge |= Qt::RightEdge; }
+ if (mouseEvent->y() < b) { edge |= Qt::TopEdge; }
+ if (mouseEvent->y() > m_window->height() - b) { edge |= Qt::BottomEdge; }
+ if (edge != 0) {
+ m_window->startSystemResize(edge);
+ return true;
+ }
+ return false;
+}
+#endif
+
bool InterfaceWindowHandler::eventFilter(QObject*, QEvent* event)
{
switch ( event->type() )
@@ -148,6 +202,19 @@ bool InterfaceWindowHandler::eventFilter(QObject*, QEvent* event)
}
break;
}
+#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
+ //Handle CSD edge behaviors
+ case QEvent::MouseMove:
+ {
+ QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
+ return CSDSetCursor(mouseEvent);
+ }
+ case QEvent::MouseButtonPress:
+ {
+ QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
+ return CSDHandleClick(mouseEvent);
+ }
+#endif
default:
break;
}
diff --git a/modules/gui/qt/maininterface/interface_window_handler.hpp b/modules/gui/qt/maininterface/interface_window_handler.hpp
index e3f0a0255a..eb5724a9f8 100644
--- a/modules/gui/qt/maininterface/interface_window_handler.hpp
+++ b/modules/gui/qt/maininterface/interface_window_handler.hpp
@@ -54,6 +54,12 @@ signals:
void interfaceFullScreenChanged(bool);
void incrementIntfUserScaleFactor(bool increment);
+private:
+#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
+ bool CSDSetCursor(QMouseEvent* mouseEvent);
+ bool CSDHandleClick(QMouseEvent* mouseEvent);
+#endif
+
protected:
intf_thread_t* p_intf = nullptr;
QWindow* m_window = nullptr;
@@ -67,6 +73,8 @@ protected:
bool m_maximizedView = false;
bool m_hideAfterCreation = false; // --qt-start-minimized
+ bool m_hasResizeCursor = false;
+
QRect m_interfaceGeometry;
};
diff --git a/modules/gui/qt/maininterface/main_interface.cpp b/modules/gui/qt/maininterface/main_interface.cpp
index 4472b6754d..b6f394fc2f 100644
--- a/modules/gui/qt/maininterface/main_interface.cpp
+++ b/modules/gui/qt/maininterface/main_interface.cpp
@@ -158,6 +158,10 @@ MainInterface::MainInterface(intf_thread_t *_p_intf , QWidget* parent, Qt::Windo
/* Should the UI stays on top of other windows */
b_interfaceOnTop = var_InheritBool( p_intf, "video-on-top" );
+#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
+ m_clientSideDecoration = ! var_InheritBool( p_intf, "qt-titlebar" );
+#endif
+
QString platformName = QGuiApplication::platformName();
#ifdef QT5_HAS_WAYLAND
@@ -195,6 +199,8 @@ MainInterface::MainInterface(intf_thread_t *_p_intf , QWidget* parent, Qt::Windo
/* VideoWidget connects for asynchronous calls */
connect( this, &MainInterface::askToQuit, THEDP, &DialogsProvider::quit, Qt::QueuedConnection );
+ connect(this, &MainInterface::interfaceFullScreenChanged, this, &MainInterface::useClientSideDecorationChanged);
+
connect( THEDP, &DialogsProvider::toolBarConfUpdated, this, &MainInterface::toolBarConfUpdated );
/** END of CONNECTS**/
@@ -257,6 +263,12 @@ MainInterface::~MainInterface()
p_intf->p_sys->p_mi = NULL;
}
+bool MainInterface::useClientSideDecoration() const
+{
+ //don't show CSD when interface is fullscreen
+ return m_clientSideDecoration && !b_interfaceFullScreen;
+}
+
void MainInterface::computeMinimumSize()
{
int minWidth = 450;
diff --git a/modules/gui/qt/maininterface/main_interface.hpp b/modules/gui/qt/maininterface/main_interface.hpp
index 43acc95233..8ac0e435e7 100644
--- a/modules/gui/qt/maininterface/main_interface.hpp
+++ b/modules/gui/qt/maininterface/main_interface.hpp
@@ -152,6 +152,7 @@ class MainInterface : public QVLCMW
Q_PROPERTY(bool mediaLibraryAvailable READ hasMediaLibrary CONSTANT)
Q_PROPERTY(bool gridView READ hasGridView WRITE setGridView NOTIFY gridViewChanged)
Q_PROPERTY(ColorSchemeModel* colorScheme READ getColorScheme CONSTANT)
+ Q_PROPERTY(bool clientSideDecoration READ useClientSideDecoration NOTIFY useClientSideDecorationChanged)
public:
/* tors */
@@ -188,7 +189,7 @@ public:
inline bool hasMediaLibrary() const { return b_hasMedialibrary; }
inline bool hasGridView() const { return m_gridView; }
inline ColorSchemeModel* getColorScheme() const { return m_colorScheme; }
-
+ bool useClientSideDecoration() const;
bool hasEmbededVideo() const;
VideoSurfaceProvider* getVideoSurfaceProvider() const;
@@ -252,6 +253,7 @@ protected:
bool b_hasMedialibrary;
bool m_gridView;
ColorSchemeModel* m_colorScheme;
+ bool m_clientSideDecoration = false;
/* States */
bool playlistVisible; ///< Is the playlist visible ?
@@ -316,6 +318,12 @@ signals:
void showRemainingTimeChanged(bool);
void gridViewChanged( bool );
void colorSchemeChanged( QString );
+ void useClientSideDecorationChanged();
+
+ /// forward window maximise query to the actual window or widget
+ void requestInterfaceMaximized();
+ /// forward window normal query to the actual window or widget
+ void requestInterfaceNormal();
void intfScaleFactorChanged();
};
diff --git a/modules/gui/qt/maininterface/main_interface_win32.cpp b/modules/gui/qt/maininterface/main_interface_win32.cpp
index a4c47cb9cc..edfe98243d 100644
--- a/modules/gui/qt/maininterface/main_interface_win32.cpp
+++ b/modules/gui/qt/maininterface/main_interface_win32.cpp
@@ -340,6 +340,19 @@ bool MainInterfaceWin32::nativeEvent(const QByteArray &eventType, void *message,
short cmd;
switch( msg->message )
{
+#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
+ case WM_NCCALCSIZE:
+ {
+ /* This is used to remove the decoration instead of using FramelessWindowHint because
+ * frameless window don't support areo snapping
+ */
+ if (useClientSideDecoration()) {
+ *result = 0;
+ return true;
+ }
+ break;
+ }
+#endif
case WM_APPCOMMAND:
cmd = GET_APPCOMMAND_LPARAM(msg->lParam);
diff --git a/modules/gui/qt/maininterface/mainui.cpp b/modules/gui/qt/maininterface/mainui.cpp
index 83d81c4ab8..500b547585 100644
--- a/modules/gui/qt/maininterface/mainui.cpp
+++ b/modules/gui/qt/maininterface/mainui.cpp
@@ -65,13 +65,15 @@ void registerAnonymousType( const char *uri, int versionMajor )
} // anonymous namespace
-MainUI::MainUI(intf_thread_t *p_intf, MainInterface *mainInterface, QObject *parent)
+MainUI::MainUI(intf_thread_t *p_intf, MainInterface *mainInterface, QWindow* interfaceWindow, QObject *parent)
: QObject(parent)
, m_intf(p_intf)
, m_mainInterface(mainInterface)
+ , m_interfaceWindow(interfaceWindow)
{
assert(m_intf);
assert(m_mainInterface);
+ assert(m_interfaceWindow);
registerQMLTypes();
}
@@ -93,7 +95,7 @@ bool MainUI::setup(QQmlEngine* engine)
rootCtx->setContextProperty( "i18n", new I18n(this) );
rootCtx->setContextProperty( "mainctx", new QmlMainContext(m_intf, m_mainInterface, this));
rootCtx->setContextProperty( "mainInterface", m_mainInterface);
- rootCtx->setContextProperty( "topWindow", m_mainInterface->windowHandle());
+ rootCtx->setContextProperty( "topWindow", m_interfaceWindow);
rootCtx->setContextProperty( "dialogProvider", DialogsProvider::getInstance());
rootCtx->setContextProperty( "recentsMedias", new VLCRecentMediaModel( m_intf, this ));
rootCtx->setContextProperty( "systemPalette", new SystemPalette(this));
diff --git a/modules/gui/qt/maininterface/mainui.hpp b/modules/gui/qt/maininterface/mainui.hpp
index 08807d6550..300faaf551 100644
--- a/modules/gui/qt/maininterface/mainui.hpp
+++ b/modules/gui/qt/maininterface/mainui.hpp
@@ -19,7 +19,7 @@ class MainUI : public QObject
Q_OBJECT
public:
- explicit MainUI(intf_thread_t *_p_intf, MainInterface* mainInterface, QObject *parent = nullptr);
+ explicit MainUI(intf_thread_t *_p_intf, MainInterface* mainInterface, QWindow* interfaceWindow, QObject *parent = nullptr);
~MainUI();
bool setup(QQmlEngine* engine);
@@ -36,6 +36,7 @@ private:
intf_thread_t* m_intf = nullptr;
MainInterface* m_mainInterface = nullptr;
+ QWindow* m_interfaceWindow = nullptr;
QQmlComponent* m_component = nullptr;
QQuickItem* m_rootItem = nullptr;
diff --git a/modules/gui/qt/maininterface/qml/BannerSources.qml b/modules/gui/qt/maininterface/qml/BannerSources.qml
index 95855a782a..f5c7fe3ae0 100644
--- a/modules/gui/qt/maininterface/qml/BannerSources.qml
+++ b/modules/gui/qt/maininterface/qml/BannerSources.qml
@@ -105,6 +105,13 @@ Widgets.NavigableFocusScope {
width: parent.width
height: VLCStyle.globalToolbar_height
+ //drag and dbl click the titlebar in CSD mode
+ Loader {
+ anchors.fill: parent
+ active: mainInterface.clientSideDecoration
+ source: "qrc:///widgets/CSDTitlebarTapNDrapHandler.qml"
+ }
+
RowLayout {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
@@ -153,6 +160,18 @@ Widgets.NavigableFocusScope {
height: globalMenuGroup.height
}
}
+
+ Loader {
+ id: globalToolbarRight
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ right: parent.right
+ rightMargin: VLCStyle.applicationHorizontalMargin
+ }
+ active: mainInterface.clientSideDecoration
+ source: "qrc:///widgets/CSDWindowButtonSet.qml"
+ }
}
Rectangle {
diff --git a/modules/gui/qt/player/qml/ResumeDialog.qml b/modules/gui/qt/player/qml/ResumeDialog.qml
index 52ee217d82..887a20da2c 100644
--- a/modules/gui/qt/player/qml/ResumeDialog.qml
+++ b/modules/gui/qt/player/qml/ResumeDialog.qml
@@ -85,12 +85,19 @@ Widgets.NavigableFocusScope {
anchors.fill: parent
color: VLCStyle.colors.setColorAlpha(VLCStyle.colors.playerBg, 0.8)
+ //drag and dbl click the titlebar in CSD mode
+ Loader {
+ anchors.fill: parent
+ active: mainInterface.clientSideDecoration
+ source: "qrc:///widgets/CSDTitlebarTapNDrapHandler.qml"
+ }
+
RowLayout {
id: layout
anchors.fill: parent
anchors.topMargin: VLCStyle.applicationVerticalMargin
anchors.leftMargin: VLCStyle.applicationHorizontalMargin + VLCStyle.margin_small
- anchors.rightMargin: VLCStyle.applicationHorizontalMargin + VLCStyle.margin_small
+ anchors.rightMargin: VLCStyle.applicationHorizontalMargin
spacing: VLCStyle.margin_small
@@ -134,6 +141,18 @@ Widgets.NavigableFocusScope {
Item {
Layout.fillWidth: true
}
+
+ Loader {
+ Layout.alignment: Qt.AlignRight | Qt.AlignTop
+ height: VLCStyle.icon_normal
+ active: mainInterface.clientSideDecoration
+ enabled: mainInterface.clientSideDecoration
+ source: "qrc:///widgets/CSDWindowButtonSet.qml"
+ onLoaded: {
+ item.color = VLCStyle.colors.playerFg
+ item.hoverColor = VLCStyle.colors.windowCSDButtonDarkBg
+ }
+ }
}
}
}
diff --git a/modules/gui/qt/player/qml/TopBar.qml b/modules/gui/qt/player/qml/TopBar.qml
index 8da792d0df..f7802ff832 100644
--- a/modules/gui/qt/player/qml/TopBar.qml
+++ b/modules/gui/qt/player/qml/TopBar.qml
@@ -65,6 +65,13 @@ Widgets.NavigableFocusScope{
anchors.rightMargin: VLCStyle.applicationHorizontalMargin
implicitHeight: rowLayout.implicitHeight
+ //drag and dbl click the titlebar in CSD mode
+ Loader {
+ anchors.fill: parent
+ active: mainInterface.clientSideDecoration
+ source: "qrc:///widgets/CSDTitlebarTapNDrapHandler.qml"
+ }
+
RowLayout {
id: rowLayout
anchors.fill: parent
@@ -132,6 +139,19 @@ Widgets.NavigableFocusScope{
spacing: VLCStyle.margin_xsmall
+ Loader {
+ //Layout.alignment: Qt.AlignRight | Qt.AlignTop
+ anchors.right: parent.right
+ height: VLCStyle.icon_normal
+ active: mainInterface.clientSideDecoration
+ enabled: mainInterface.clientSideDecoration
+ source: "qrc:///widgets/CSDWindowButtonSet.qml"
+ onLoaded: {
+ item.color = VLCStyle.colors.playerFg
+ item.hoverColor = VLCStyle.colors.windowCSDButtonDarkBg
+ }
+ }
+
Row {
//Layout.alignment: Qt.AlignRight | Qt.AlignTop
anchors.right: parent.right
diff --git a/modules/gui/qt/qt.cpp b/modules/gui/qt/qt.cpp
index c05df6e2fa..f51f11d1ae 100644
--- a/modules/gui/qt/qt.cpp
+++ b/modules/gui/qt/qt.cpp
@@ -234,6 +234,10 @@ static void ShowDialog ( intf_thread_t *, int, int, intf_dialog_args_t * );
#define AUTORAISE_ON_PLAYBACK_LONGTEXT N_( "This option allows the interface to be raised automatically " \
"when a video/audio playback starts, or never." )
+#define QT_CLIENT_SIDE_DECORATION_TEXT N_( "Enable window titlebar" )
+#define QT_CLIENT_SIDE_DECORATION_LONGTEXT N_( "This option enables the title bar. Disabling it will remove " \
+ "the titlebar and move window buttons within the interface (Client Side Decoration)" )
+
#define FULLSCREEN_CONTROL_PIXELS N_( "Fullscreen controller mouse sensitivity" )
#define CONTINUE_PLAYBACK_TEXT N_("Continue playback?")
@@ -328,6 +332,16 @@ vlc_module_begin ()
false /* advanced mode only */)
#endif
+#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
+ add_bool( "qt-titlebar",
+#ifdef _WIN32
+ false /* use CSD by default on windows */,
+#else
+ true /* but not on linux */,
+#endif
+ QT_CLIENT_SIDE_DECORATION_TEXT, QT_CLIENT_SIDE_DECORATION_LONGTEXT, false )
+#endif
+
add_bool( "qt-embedded-open", false, QT_NATIVEOPEN_TEXT,
QT_NATIVEOPEN_TEXT, false )
diff --git a/modules/gui/qt/style/VLCColors.qml b/modules/gui/qt/style/VLCColors.qml
index efaa276fe4..19094ad519 100644
--- a/modules/gui/qt/style/VLCColors.qml
+++ b/modules/gui/qt/style/VLCColors.qml
@@ -112,6 +112,9 @@ Item {
mainInterface.colorScheme.setAvailableColorSchemes(["system", "day", "night"])
}
+ property color windowCSDButtonDarkBg: "#80484848"
+ property color windowCSDButtonLightBg: "#80DADADA"
+ property color windowCSDButtonBg: isThemeDark ? windowCSDButtonDarkBg : windowCSDButtonLightBg
state: mainInterface.colorScheme.current
states: [
diff --git a/modules/gui/qt/style/VLCStyle.qml b/modules/gui/qt/style/VLCStyle.qml
index 590bba9d68..df3a3c072e 100644
--- a/modules/gui/qt/style/VLCStyle.qml
+++ b/modules/gui/qt/style/VLCStyle.qml
@@ -180,8 +180,8 @@ Item {
property int appHeight: 0
//global application margin "safe area"
- property int applicationHorizontalMargin: 0
- property int applicationVerticalMargin: 0
+ property int applicationHorizontalMargin: mainInterface.clientSideDecoration ? dp(5, scale) : 0
+ property int applicationVerticalMargin: mainInterface.clientSideDecoration ? dp(5, scale) : 0
property int globalToolbar_height: dp(32, scale)
property int localToolbar_height: dp(40, scale)
diff --git a/modules/gui/qt/vlc.qrc b/modules/gui/qt/vlc.qrc
index 8c495e9c55..fcf9c86592 100644
--- a/modules/gui/qt/vlc.qrc
+++ b/modules/gui/qt/vlc.qrc
@@ -180,6 +180,9 @@
<qresource prefix="/widgets">
<file alias="BannerTabButton.qml">widgets/qml/BannerTabButton.qml</file>
<file alias="BusyIndicatorExt.qml">widgets/qml/BusyIndicatorExt.qml</file>
+ <file alias="CSDWindowButton.qml">widgets/qml/CSDWindowButton.qml</file>
+ <file alias="CSDWindowButtonSet.qml">widgets/qml/CSDWindowButtonSet.qml</file>
+ <file alias="CSDTitlebarTapNDrapHandler.qml">widgets/qml/CSDTitlebarTapNDrapHandler.qml</file>
<file alias="GridItem.qml">widgets/qml/GridItem.qml</file>
<file alias="ListItem.qml">widgets/qml/ListItem.qml</file>
<file alias="DrawerExt.qml">widgets/qml/DrawerExt.qml</file>
diff --git a/modules/gui/qt/widgets/qml/CSDTitlebarTapNDrapHandler.qml b/modules/gui/qt/widgets/qml/CSDTitlebarTapNDrapHandler.qml
new file mode 100644
index 0000000000..392d7619e9
--- /dev/null
+++ b/modules/gui/qt/widgets/qml/CSDTitlebarTapNDrapHandler.qml
@@ -0,0 +1,46 @@
+/*****************************************************************************
+ * Copyright (C) 2020 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+//CSD is only supported on Qt 5.15
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Item {
+ TapHandler {
+ onTapped: {
+ if (tapCount === 2) {
+ if ((topWindow.visibility & Window.Maximized) !== 0) {
+ mainInterface.requestInterfaceNormal()
+ } else {
+ mainInterface.requestInterfaceMaximized()()
+ }
+ }
+ }
+ gesturePolicy: TapHandler.DragThreshold
+ }
+ DragHandler {
+ target: null
+ grabPermissions: TapHandler.CanTakeOverFromAnything
+ onActiveChanged: {
+ if (active) {
+ topWindow.startSystemMove();
+ }
+ }
+ }
+
+}
diff --git a/modules/gui/qt/widgets/qml/CSDWindowButton.qml b/modules/gui/qt/widgets/qml/CSDWindowButton.qml
new file mode 100644
index 0000000000..361d87d2f2
--- /dev/null
+++ b/modules/gui/qt/widgets/qml/CSDWindowButton.qml
@@ -0,0 +1,57 @@
+/*****************************************************************************
+ * Copyright (C) 2020 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+import QtQuick 2.11
+import QtQuick.Controls 2.4
+import QtQuick.Templates 2.4 as T
+import QtQuick.Layouts 1.11
+
+import "qrc:///style/"
+
+
+T.TabButton {
+ id: control
+
+ property color color: VLCStyle.colors.text
+ property color hoverColor: VLCStyle.colors.windowCSDButtonBg
+ property string iconTxt: ""
+
+ padding: 0
+ width: VLCStyle.dp(40, VLCStyle.scale)
+ implicitWidth: contentItem.implicitWidth
+ implicitHeight: contentItem.implicitHeight
+
+ background: Rectangle {
+ height: control.height
+ width: control.width
+ color: !control.hovered ? "transparent"
+ : control.pressed ? (VLCStyle.isThemeDark ? Qt.lighter(control.hoverColor, 1.2)
+ : Qt.darker(control.hoverColor, 1.2)
+ )
+ : control.hoverColor
+ }
+
+ contentItem: Item {
+ IconLabel {
+ id: icon
+ anchors.centerIn: parent
+ text: control.iconTxt
+ font.pixelSize: VLCIcons.pixelSize(VLCStyle.dp(20, VLCStyle.scale))
+ color: control.color
+ }
+ }
+}
diff --git a/modules/gui/qt/widgets/qml/CSDWindowButtonSet.qml b/modules/gui/qt/widgets/qml/CSDWindowButtonSet.qml
new file mode 100644
index 0000000000..ee15576821
--- /dev/null
+++ b/modules/gui/qt/widgets/qml/CSDWindowButtonSet.qml
@@ -0,0 +1,62 @@
+/*****************************************************************************
+ * Copyright (C) 2020 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+import QtQuick 2.11
+import QtQuick.Window 2.11
+
+import "qrc:///style/"
+
+Row {
+ id: windowButtonGroup
+
+ spacing: 0
+ padding: 0
+
+ property color color: VLCStyle.colors.text
+ property color hoverColor: VLCStyle.colors.windowCSDButtonBg
+
+ CSDWindowButton {
+ iconTxt: VLCIcons.window_minimize
+ onClicked: topWindow.showMinimized()
+ height: windowButtonGroup.height
+ color: windowButtonGroup.color
+ hoverColor: windowButtonGroup.hoverColor
+ }
+
+ CSDWindowButton {
+ iconTxt: (topWindow.visibility & Window.Maximized) ? VLCIcons.window_restore :VLCIcons.window_maximize
+ onClicked: {
+ if (topWindow.visibility & Window.Maximized) {
+ mainInterface.requestInterfaceNormal()
+ } else {
+ mainInterface.requestInterfaceMaximized()
+ }
+ }
+ height: windowButtonGroup.height
+ color: windowButtonGroup.color
+ hoverColor: windowButtonGroup.hoverColor
+ }
+
+ CSDWindowButton {
+ id: closeButton
+ iconTxt: VLCIcons.window_close
+ onClicked: topWindow.close()
+ height: windowButtonGroup.height
+ color: closeButton.hovered ? "white" : windowButtonGroup.color
+ hoverColor: "red"
+ }
+}
More information about the vlc-commits
mailing list