[vlc-commits] [Git][videolan/vlc][master] 8 commits: qt: introduce `PointingToolTipAttached`

Steve Lhomme (@robUx4) gitlab at videolan.org
Thu Jul 16 16:58:34 UTC 2026



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
9869b724 by Fatih Uzunoglu at 2026-07-16T16:26:14+00:00
qt: introduce `PointingToolTipAttached`

Akin to `QQuickToolTipAttached`

- - - - -
ad2f05ea by Fatih Uzunoglu at 2026-07-16T16:26:14+00:00
qt: register `PointingToolTipAttached` as qml type

- - - - -
4b81481d by Fatih Uzunoglu at 2026-07-16T16:26:14+00:00
qml: set attached pointing tool tip instance in `MainInterface`

This is the same we are doing with attached regular tool tip.

- - - - -
55301b4e by Fatih Uzunoglu at 2026-07-16T16:26:14+00:00
qml: use attached pointing tool tip in `SliderExt`

- - - - -
82e5dafd by Fatih Uzunoglu at 2026-07-16T16:26:14+00:00
qml: use attached pointing tool tip in `SliderBar`

- - - - -
4cfb3a69 by Fatih Uzunoglu at 2026-07-16T16:26:14+00:00
qml: use attached pointing tool tip in `Bookmarks`

- - - - -
0ef901ca by Fatih Uzunoglu at 2026-07-16T16:26:14+00:00
qml: add note regarding window type in `ToolTipExt`

- - - - -
51d56322 by Fatih Uzunoglu at 2026-07-16T16:26:14+00:00
qml: add note regarding window type in `PointingTooltip`

- - - - -


11 changed files:

- modules/gui/qt/Makefile.am
- modules/gui/qt/maininterface/mainui.cpp
- modules/gui/qt/maininterface/qml/MainInterface.qml
- modules/gui/qt/medialibrary/qml/Bookmarks.qml
- modules/gui/qt/meson.build
- modules/gui/qt/player/qml/SliderBar.qml
- modules/gui/qt/player/qml/controlbarcontrols/VolumeWidget.qml
- + modules/gui/qt/util/pointingtooltipattached.hpp
- modules/gui/qt/widgets/qml/PointingTooltip.qml
- modules/gui/qt/widgets/qml/SliderExt.qml
- modules/gui/qt/widgets/qml/ToolTipExt.qml


Changes:

=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -365,6 +365,7 @@ libqt_plugin_la_SOURCES = \
 	util/textureproviderindirection.hpp \
 	util/sgmanipulator.cpp \
 	util/sgmanipulator.hpp \
+	util/pointingtooltipattached.hpp \
 	widgets/native/animators.cpp \
 	widgets/native/animators.hpp \
 	widgets/native/customwidgets.cpp widgets/native/customwidgets.hpp \
@@ -527,6 +528,7 @@ nodist_libqt_plugin_la_SOURCES = \
 	util/kirigamiwheelhandler.moc.cpp \
 	util/textureproviderindirection.moc.cpp \
 	util/sgmanipulator.moc.cpp \
+	util/pointingtooltipattached.moc.cpp \
 	widgets/native/animators.moc.cpp \
 	widgets/native/csdthemeimage.moc.cpp \
 	widgets/native/customwidgets.moc.cpp \


=====================================
modules/gui/qt/maininterface/mainui.cpp
=====================================
@@ -55,6 +55,7 @@
 #include "util/textureproviderobserver.hpp"
 #include "util/textureproviderindirection.hpp"
 #include "util/sgmanipulator.hpp"
+#include "util/pointingtooltipattached.hpp"
 
 #include "dialogs/help/aboutmodel.hpp"
 #include "dialogs/dialogs_provider.hpp"
@@ -433,6 +434,7 @@ void MainUI::registerQMLTypes()
         // @uri VLC.Widgets
         qmlRegisterType<CSDThemeImage>(uri, versionMajor, versionMinor, "CSDThemeImage");
         qmlRegisterType<ViewBlockingRectangle>( uri, versionMajor, versionMinor, "ViewBlockingRectangle" );
+        qmlRegisterTypesAndRevisions<PointingToolTipAttached>( uri, versionMajor );
 
         qmlRegisterModule(uri, versionMajor, versionMinor);
         qmlProtectModule(uri, versionMajor);


=====================================
modules/gui/qt/maininterface/qml/MainInterface.qml
=====================================
@@ -217,6 +217,21 @@ Item {
             }
         }
 
+        Widgets.PointingTooltip {
+            parent: null
+            z: 99
+
+            colorContext.palette: parent && parent.colorContext ? parent.colorContext.palette
+                                                                : VLCStyle.palette
+
+            Widgets.PointingToolTipAttached.instance: this
+
+            Component.onDestruction: {
+                console.assert(Widgets.PointingToolTipAttached.instance === this)
+                Widgets.PointingToolTipAttached.instance = null
+            }
+        }
+
         Loader {
             id: playlistWindowLoader
             asynchronous: true


=====================================
modules/gui/qt/medialibrary/qml/Bookmarks.qml
=====================================
@@ -42,16 +42,9 @@ Item {
     visible: false
     opacity: 0
 
-    Widgets.PointingTooltip {
-        id: timeTooltip
-
-        //tooltip is a Popup, palette should be passed explicitly
-        colorContext.palette: theme.palette
-
-        visible: control.bookmarkHovered
-        text: control.bookmarkText
-        pos: Qt.point(control.bookmarkPosition, - yShift)
-    }
+    Widgets.PointingToolTipAttached.visible: control.bookmarkHovered
+    Widgets.PointingToolTipAttached.text: control.bookmarkText
+    Widgets.PointingToolTipAttached.pos: Qt.point(control.bookmarkPosition, - yShift)
 
     transitions: [
         Transition {


=====================================
modules/gui/qt/meson.build
=====================================
@@ -158,6 +158,7 @@ moc_headers = files(
     'util/kirigamiwheelhandler.hpp',
     'util/textureproviderindirection.hpp',
     'util/sgmanipulator.hpp',
+    'util/pointingtooltipattached.hpp',
     'widgets/native/animators.hpp',
     'widgets/native/csdthemeimage.hpp',
     'widgets/native/customwidgets.hpp',
@@ -513,6 +514,7 @@ qt_plugin_sources = files(
     'util/textureproviderindirection.hpp',
     'util/sgmanipulator.cpp',
     'util/sgmanipulator.hpp',
+    'util/pointingtooltipattached.hpp',
     'widgets/native/animators.cpp',
     'widgets/native/animators.hpp',
     'widgets/native/customwidgets.cpp',


=====================================
modules/gui/qt/player/qml/SliderBar.qml
=====================================
@@ -40,7 +40,7 @@ T.ProgressBar {
     readonly property real _scaledSeekPointsRadius: _seekPointsRadius * _hoveredScalingFactor
 
     property bool _currentChapterHovered: false
-    property real _tooltipPosition: timeTooltip.pos.x / width
+    property real _tooltipPosition: (Widgets.PointingToolTipAttached.instance?.pos.x ?? 0.0) / width
 
     property color backgroundColor: theme.bg.primary
     property real touchHandlerMargin: VLCStyle.touchHandlerMargin
@@ -72,35 +72,31 @@ T.ProgressBar {
         onTriggered: control._isSeekPointsShown = false
     }
 
-    Widgets.PointingTooltip {
-        id: timeTooltip
+    Widgets.PointingToolTipAttached.visible: (hoverHandler.hovered || control.visualFocus || dragHandler.active)
 
-        //tooltip is a Popup, palette should be passed explicitly
-        colorContext.palette: theme.palette
+    Widgets.PointingToolTipAttached.text: {
+        if (!Widgets.PointingToolTipAttached.instance?.visible)
+            return ""
 
-        visible: hoverHandler.hovered || control.visualFocus || dragHandler.active
+        let _text
 
-        text: {
-            let _text
+        const length = Player.length
+        if (hoverHandler.hovered)
+            _text = length.scale(Widgets.PointingToolTipAttached.instance.pos.x / control.width)
+        else
+            _text = Player.time
 
-            const length = Player.length
-            if (hoverHandler.hovered)
-                _text = length.scale(pos.x / control.width)
-            else
-                _text = Player.time
+        _text = _text.formatHMS(length.isSubSecond() ? VLCTick.SubSecondFormattedAsMS : 0)
 
-            _text = _text.formatHMS(length.isSubSecond() ? VLCTick.SubSecondFormattedAsMS : 0)
+        if (Player.hasChapters)
+            _text += " - " + Player.chapters.getNameAtPosition(control._tooltipPosition)
 
-            if (Player.hasChapters)
-                _text += " - " + Player.chapters.getNameAtPosition(control._tooltipPosition)
-
-            return _text
-        }
-
-        pos: Qt.point(hoverHandler.hovered ? Helpers.clamp(hoverHandler.point.position.x, 0, control.availableWidth)
-                                                        : (control.visualPosition * control.width), 0)
+        return _text
     }
 
+    Widgets.PointingToolTipAttached.pos: Qt.point(hoverHandler.hovered ? Helpers.clamp(hoverHandler.point.position.x, 0, control.availableWidth)
+                                                                       : (control.visualPosition * control.width), 0)
+
     FSM {
         id: fsm
         signal playerUpdatePosition(real position)


=====================================
modules/gui/qt/player/qml/controlbarcontrols/VolumeWidget.qml
=====================================
@@ -196,7 +196,7 @@ T.Pane {
                 function onVolumeChanged() { volControl._syncVolumeWithPlayer() }
             }
 
-            Binding on toolTip.visible {
+            Binding on Widgets.PointingToolTipAttached.visible {
                 when: sliderMouseArea.pressed
                 value: true
             }


=====================================
modules/gui/qt/util/pointingtooltipattached.hpp
=====================================
@@ -0,0 +1,184 @@
+/*****************************************************************************
+ * Copyright (C) 2026 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.
+ *****************************************************************************/
+#ifndef POINTINGTOOLTIPATTACHED_HPP
+#define POINTINGTOOLTIPATTACHED_HPP
+
+#include <QQmlProperty>
+#include <QPointF>
+#include <QQuickItem>
+#include <QString>
+#include <QtQml>
+
+class PointingToolTipAttached : public QObject
+{
+    Q_OBJECT
+
+    // Note that instance is static:
+    Q_PROPERTY(QObject* instance MEMBER m_instance NOTIFY instanceChanged FINAL)
+
+    // Note that these properties are not bidirectionally synchronized. If you
+    // want to get the actual values that the tool tip uses, access it through
+    // the `instance` property:
+    Q_PROPERTY(QPointF pos MEMBER m_pos WRITE setPos NOTIFY posChanged FINAL)
+    Q_PROPERTY(bool visible MEMBER m_visible WRITE setVisible NOTIFY visibleChanged FINAL)
+    Q_PROPERTY(QString text MEMBER m_text WRITE setText NOTIFY textChanged FINAL)
+
+    // The name `PointingToolTip` is not used to not conflict with the already existing type.
+    QML_NAMED_ELEMENT(PointingToolTipAttached)
+    QML_UNCREATABLE("PointingToolTipAttached is meant to be used as attached.")
+    QML_ATTACHED(PointingToolTipAttached)
+
+public:
+    explicit PointingToolTipAttached(QObject *parent = nullptr)
+        : QObject(parent)
+    {
+        // This is an unlikely case, but we can still handle it:
+        connect(this, &PointingToolTipAttached::instanceChanged, this, [this]() {
+            m_posProperty.reset();
+            m_visibleProperty.reset();
+            m_textProperty.reset();
+            m_parentProperty.reset();
+
+            if (m_instance)
+            {
+                setPos(m_pos);
+                setText(m_text);
+                setVisible(m_visible);
+            }
+        });
+    }
+
+    static PointingToolTipAttached *qmlAttachedProperties(QObject* parent)
+    {
+        return new PointingToolTipAttached(parent);
+    }
+
+    void setPos(const QPointF& point)
+    {
+        if (m_pos != point)
+        {
+            m_pos = point;
+            emit posChanged();
+        }
+
+        if (instanceBoundToParent())
+        {
+            if (!m_posProperty)
+                m_posProperty = QQmlProperty(m_instance, QStringLiteral("pos"));
+
+            assert(m_posProperty->isWritable());
+
+            m_posProperty->write(point);
+        }
+    }
+
+    void setText(const QString& text)
+    {
+        if (m_text != text)
+        {
+            m_text = text;
+            emit textChanged();
+        }
+
+        if (instanceBoundToParent())
+        {
+            if (!m_textProperty)
+                m_textProperty = QQmlProperty(m_instance, QStringLiteral("text"));
+
+            assert(m_textProperty->isWritable());
+
+            m_textProperty->write(text);
+        }
+    }
+
+    // Setting visibility binds the attached tool tip to the parent of the
+    // attached tool tip:
+    void setVisible(bool visible)
+    {
+        if (m_visible != visible)
+        {
+            m_visible = visible;
+            emit visibleChanged();
+        }
+
+        assert(m_instance);
+
+        if (!m_visibleProperty)
+            m_visibleProperty = QQmlProperty(m_instance, QStringLiteral("visible"));
+
+        assert(m_visibleProperty->isWritable());
+
+        // Akin to `QQuickToolTipAttached`:
+        if (visible)
+        {
+            setPos(m_pos);
+            setText(m_text);
+
+            if (!m_parentProperty)
+                m_parentProperty = QQmlProperty(m_instance, QStringLiteral("parent"));
+
+            assert(m_parentProperty->isWritable());
+
+            m_parentProperty->write(QVariant::fromValue(qobject_cast<QQuickItem*>(parent())));
+
+            m_visibleProperty->write(true);
+        }
+        else
+        {
+            if (instanceBoundToParent())
+                m_visibleProperty->write(false);
+        }
+    }
+
+signals:
+    void instanceChanged();
+
+    void zChanged();
+    void posChanged();
+    void visibleChanged();
+    void textChanged();
+
+protected:
+    bool instanceBoundToParent() const
+    {
+        assert(m_instance);
+
+        if (!m_parentProperty)
+            m_parentProperty = QQmlProperty(m_instance, QStringLiteral("parent"));
+
+        assert(m_parentProperty->isValid());
+
+        return (m_parentProperty->read().value<QQuickItem*>() == qobject_cast<QQuickItem*>(parent()));
+    }
+
+private:
+    static inline QPointer<QObject> m_instance;
+
+    QPointF m_pos;
+    bool m_visible = false;
+    QString m_text;
+
+    std::optional<QQmlProperty> m_posProperty;
+    std::optional<QQmlProperty> m_visibleProperty;
+    std::optional<QQmlProperty> m_textProperty;
+    mutable std::optional<QQmlProperty> m_parentProperty;
+};
+
+QML_DECLARE_TYPEINFO(PointingToolTipAttached, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // POINTINGTOOLTIPATTACHED_HPP


=====================================
modules/gui/qt/widgets/qml/PointingTooltip.qml
=====================================
@@ -20,6 +20,7 @@ import QtQuick.Controls
 
 import VLC.Style
 
+// NOTE: Attached pointing tool tip must be used if the popup type is `Popup.Window`.
 ToolTipExt {
     id: pointingTooltip
 


=====================================
modules/gui/qt/widgets/qml/SliderExt.qml
=====================================
@@ -21,7 +21,7 @@
 import QtQuick
 import QtQuick.Templates as T
 
-
+import VLC.Widgets as Widgets
 import VLC.Style
 import VLC.Util
 
@@ -51,8 +51,6 @@ T.Slider {
     // else tooltip will always be shown at current value.
     property bool toolTipFollowsMouse: false
 
-    property alias toolTip: toolTip
-
     // toolTipTextProvider -> function(value)
     // arg "value" is between from and to, this is "value"
     // at which pointing tool tip is currently shown
@@ -138,26 +136,17 @@ T.Slider {
         target: background
     }
 
-    PointingTooltip {
-       id: toolTip
-
-       z: 1 // without this tooltips get placed below root's parent popup (if any)
-
-       pos: Qt.point(control._tooltipX, control.handle.height / 2)
-
-       visible: hoverHandler.hovered || control.visualFocus || control.pressed
-
-       text: {
-           if (!visible) return ""
-
-           // position is only measured till half of handle width
-           // pos.x may go beyond the position at the edges
-           const p = Helpers.clamp(control.positionAt(pos.x), 0.0, 1.0)
-           const v = control.valueAt(p)
-           return control.toolTipTextProvider(v)
-       }
-
-       //tooltip is a Popup, palette should be passed explicitly
-       colorContext.palette: theme.palette
+    Widgets.PointingToolTipAttached.pos: Qt.point(control._tooltipX, control.handle.height / 2)
+    Widgets.PointingToolTipAttached.visible: (hoverHandler.hovered || control.visualFocus || control.pressed)
+    Widgets.PointingToolTipAttached.text: {
+        if (!Widgets.PointingToolTipAttached.instance?.visible)
+            return ""
+
+        // position is only measured till half of handle width
+        // pos.x may go beyond the position at the edges
+        const p = Helpers.clamp(control.positionAt(Widgets.PointingToolTipAttached.instance.pos.x), 0.0, 1.0)
+        const v = control.valueAt(p)
+        return control.toolTipTextProvider(v)
     }
+
 }


=====================================
modules/gui/qt/widgets/qml/ToolTipExt.qml
=====================================
@@ -22,6 +22,7 @@ import QtQuick.Templates as T
 import VLC.MainInterface
 import VLC.Style
 
+// NOTE: Attached tool tip must be used if the popup type is `Popup.Window`.
 T.ToolTip {
     id: control
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/a4260c0dcfd61288552e81f36a10ade8ce5cff5f...51d563221b543ef87888a8e21aada922948929ba

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/a4260c0dcfd61288552e81f36a10ade8ce5cff5f...51d563221b543ef87888a8e21aada922948929ba
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list