feat(qt): explorer collapse caret tooltip and collapsed filter strip

The Library Explorer caret now says what it does on hover (Show/Hide
filter panels, all eight locales). While the panels are collapsed with
filters applied, a single-line strip of removable chips stays visible
next to the caret (top of the results area for the left/right rail
layouts), so hiding the panels never hides what is filtering the
results. The chip is the empty-state delegate extracted into a shared
component; removal reuses clearFilterChip verbatim.

cc
This commit is contained in:
vicrodh committed 2026-08-30 16:17:29 -06:00
1 parent 41b8e7e6f8
commit 9486346130
9 files changed
+171 -42

No files matched your search

@@ -6248,3 +6248,9 @@ msgstr "Mixerregler auswählen"
msgid "Change mixer control"
msgstr "Mixerregler ändern"
msgid "Show filter panels"
msgstr "Filterbereiche anzeigen"
msgid "Hide filter panels"
msgstr "Filterbereiche ausblenden"
@@ -1868,3 +1868,9 @@ msgstr "Choose mixer control"
msgid "Change mixer control"
msgstr "Change mixer control"
msgid "Show filter panels"
msgstr "Show filter panels"
msgid "Hide filter panels"
msgstr "Hide filter panels"
@@ -6254,3 +6254,9 @@ msgstr "Elegir control de mezclador"
msgid "Change mixer control"
msgstr "Cambiar control de mezclador"
msgid "Show filter panels"
msgstr "Mostrar los paneles de filtros"
msgid "Hide filter panels"
msgstr "Ocultar los paneles de filtros"
@@ -6246,3 +6246,9 @@ msgstr "Choisir la commande de mixage"
msgid "Change mixer control"
msgstr "Changer la commande de mixage"
msgid "Show filter panels"
msgstr "Afficher les panneaux de filtres"
msgid "Hide filter panels"
msgstr "Masquer les panneaux de filtres"
@@ -6230,3 +6230,9 @@ msgstr "ミキサーコントロールを選択"
msgid "Change mixer control"
msgstr "ミキサーコントロールを変更"
msgid "Show filter panels"
msgstr "フィルターパネルを表示"
msgid "Hide filter panels"
msgstr "フィルターパネルを非表示"
@@ -6236,3 +6236,9 @@ msgstr "Mixerregelaar kiezen"
msgid "Change mixer control"
msgstr "Mixerregelaar wijzigen"
msgid "Show filter panels"
msgstr "Filterpanelen tonen"
msgid "Hide filter panels"
msgstr "Filterpanelen verbergen"
@@ -6246,3 +6246,9 @@ msgstr "Escolher controlo do misturador"
msgid "Change mixer control"
msgstr "Alterar controlo do misturador"
msgid "Show filter panels"
msgstr "Mostrar os painéis de filtros"
msgid "Hide filter panels"
msgstr "Ocultar os painéis de filtros"
@@ -6266,3 +6266,9 @@ msgstr "Выбрать регулятор микшера"
msgid "Change mixer control"
msgstr "Изменить регулятор микшера"
msgid "Show filter panels"
msgstr "Показать панели фильтров"
msgid "Hide filter panels"
msgstr "Скрыть панели фильтров"
+123 -42
View File
@@ -157,6 +157,12 @@ Item {
root.view.clearFilter()
}
readonly property var activeFilterChips: buildActiveFilterChips()
// The collapsed strip keeps one line of the applied filters visible, so
// hiding the panels never hides WHAT is currently filtering the results.
readonly property bool collapsedChipsActive:
(root.view ? root.view.genresBrowserCollapsed : false)
&& activeFilterChips.length > 0
readonly property int chipRowThickness: 36
function caretName() {
var collapsed = root.view && root.view.genresBrowserCollapsed
if (filterPosition === "bottom") return collapsed ? "chevron-up" : "chevron-down"
@@ -182,6 +188,48 @@ Item {
onToggled: function(key, modifiers) { root.toggleFacet(facetIndex, key, modifiers) }
}
// One removable applied-filter chip — shared between the collapsed strip
// and the "no albums match" empty state.
component ActiveFilterChip: Rectangle {
id: chip
required property var modelData
width: chipContent.implicitWidth + 18
height: 28
radius: 6
color: chipMouse.containsMouse
? theme.surfaceHover
: (theme.ambientOn ? theme.surfaceElevatedA50
: theme.surfaceElevated)
border.width: 1
border.color: theme.ambientOn ? theme.frostBorder : theme.borderSubtle
Row {
id: chipContent
anchors.centerIn: parent
spacing: 7
Text {
text: chip.modelData.label
color: theme.textPrimary
font.pixelSize: theme.fontLegal
elide: Text.ElideRight
}
QbzIcon {
name: "x"
width: 11
height: 11
anchors.verticalCenter: parent.verticalCenter
tintName: chipMouse.containsMouse ? "textPrimary" : "muted"
}
}
MouseArea {
id: chipMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.clearFilterChip(chip.modelData)
}
}
Component {
id: horizontalBrowser
Row {
@@ -230,7 +278,11 @@ Item {
: Math.max(0, root.width - 64)
height: root.verticalFilters
? Math.max(0, root.height - 20)
: (collapsed ? root.collapsedThickness : root.horizontalHeight)
: (collapsed
? (root.collapsedChipsActive
? root.collapsedThickness + root.chipRowThickness
: root.collapsedThickness)
: root.horizontalHeight)
Loader {
visible: !browser.collapsed
@@ -274,9 +326,64 @@ Item {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.view.genresBrowserCollapsed = !root.view.genresBrowserCollapsed
onContainsMouseChanged: tips.hover(
containsMouse, parent, "explorer-collapse",
browser.collapsed
? QbzSession.tr("Show filter panels", QbzSession.trRev)
: QbzSession.tr("Hide filter panels", QbzSession.trRev))
onClicked: {
// The label flips with the state; a bubble captured before
// the click would keep lying until the pointer re-enters.
tips.hide("explorer-collapse")
root.view.genresBrowserCollapsed = !root.view.genresBrowserCollapsed
}
}
}
// Applied filters, one line, while the panels are hidden. Each chip
// removes its filter immediately; overflow stays on this line and
// wheel-scrolls, it never wraps into the reclaimed space.
Item {
visible: !root.verticalFilters && root.collapsedChipsActive
x: 0
y: root.filterPosition === "bottom" ? parent.height - 28 : 0
width: parent.width
height: 28
ListView {
anchors.horizontalCenter: parent.horizontalCenter
width: Math.min(contentWidth, parent.width)
height: parent.height
orientation: ListView.Horizontal
spacing: 8
clip: true
boundsBehavior: Flickable.StopAtBounds
model: root.activeFilterChips
delegate: ActiveFilterChip { }
}
}
}
// The vertical rail is 24px wide — no room for a readable line there, so
// left/right layouts put the same strip across the top of the results.
Item {
visible: root.verticalFilters && root.collapsedChipsActive
x: results.x
y: 10
width: results.width
height: 28
ListView {
anchors.horizontalCenter: parent.horizontalCenter
width: Math.min(contentWidth, parent.width)
height: parent.height
orientation: ListView.Horizontal
spacing: 8
clip: true
boundsBehavior: Flickable.StopAtBounds
model: root.activeFilterChips
delegate: ActiveFilterChip { }
}
}
Item {
@@ -288,14 +395,16 @@ Item {
x: root.verticalFilters && root.filterPosition === "left"
? browser.x + browser.width + 8 : 32
y: !root.verticalFilters && root.filterPosition === "top"
? browser.y + browser.height + 8 : 10
? browser.y + browser.height + 8
: (root.verticalFilters && root.collapsedChipsActive
? 10 + root.chipRowThickness : 10)
width: root.verticalFilters
? (root.filterPosition === "left"
? Math.max(0, root.width - x - 32)
: Math.max(0, browser.x - x - 8))
: Math.max(0, root.width - 64)
height: root.verticalFilters
? Math.max(0, root.height - 20)
? Math.max(0, root.height - y - 10)
: (root.filterPosition === "bottom"
? Math.max(0, browser.y - y - 8)
: Math.max(0, root.height - y - 10))
@@ -342,44 +451,7 @@ Item {
Repeater {
model: root.activeFilterChips
delegate: Rectangle {
required property var modelData
width: chipRow.implicitWidth + 18
height: 28
radius: 6
color: chipArea.containsMouse
? theme.surfaceHover
: (theme.ambientOn ? theme.surfaceElevatedA50
: theme.surfaceElevated)
border.width: 1
border.color: theme.ambientOn ? theme.frostBorder : theme.borderSubtle
Row {
id: chipRow
anchors.centerIn: parent
spacing: 7
Text {
text: parent.parent.modelData.label
color: theme.textPrimary
font.pixelSize: theme.fontLegal
elide: Text.ElideRight
}
QbzIcon {
name: "x"
width: 11
height: 11
anchors.verticalCenter: parent.verticalCenter
tintName: chipArea.containsMouse ? "textPrimary" : "muted"
}
}
MouseArea {
id: chipArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.clearFilterChip(parent.modelData)
}
}
delegate: ActiveFilterChip { }
}
}
@@ -429,4 +501,13 @@ Item {
albums: root.view.genreAlbumsVisible
}
}
// Hover tooltip for the collapse caret. The overlay takes no pointer and
// owns no animator, so an idle one costs nothing (LocalChrome.qml does the
// same for the header's controls; this tab body is outside its bounds).
QbzTooltip {
id: tips
anchors.fill: parent
z: 900
}
}