vlc/modules/gui/qt/maininterface/qml/BannerSources.qml

512 lines
21 KiB
QML

/*****************************************************************************
* Copyright (C) 2019 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 QtGraphicalEffects 1.0
import QtQml.Models 2.11
import QtQuick.Window 2.11
import org.videolan.vlc 0.1
import org.videolan.compat 0.1
import "qrc:///style/"
import "qrc:///playlist/" as PL
import "qrc:///widgets/" as Widgets
import "qrc:///menus/" as Menus
import "qrc:///util/Helpers.js" as Helpers
FocusScope {
id: root
height: VLCStyle.applicationVerticalMargin
+ (menubar.visible ? menubar.height : 0)
+ VLCStyle.globalToolbar_height
+ VLCStyle.localToolbar_height
property int selectedIndex: 0
property int subSelectedIndex: 0
property alias sortMenu: sortControl.menu
property alias sortModel: sortControl.model
property var contentModel
property alias isViewMultiView: list_grid_btn.visible
property alias model: pLBannerSources.model
property var extraLocalActions: undefined
property alias localMenuDelegate: localMenuGroup.sourceComponent
// For now, used for d&d functionality
// Not strictly necessary to set
property PL.PlaylistListView plListView: null
property bool _showCSD: MainCtx.clientSideDecoration && !(MainCtx.intfMainWindow.visibility === Window.FullScreen)
signal itemClicked(int index)
signal toogleMenu()
// Triggered when the toogleView button is selected
function toggleView () {
MainCtx.gridView = !MainCtx.gridView
}
function search() {
searchBox.state = "expanded"
}
ColorContext {
id: theme
colorSet: ColorContext.Window
}
BindingCompat {
property: "searchPattern"
value: searchBox.searchPattern
when: !!contentModel
Component.onCompleted: {
// restoreMode is only available in Qt >= 5.14
if ("restoreMode" in this)
this.restoreMode = Binding.RestoreBindingOrValue
target = Qt.binding(function() { return !!contentModel ? contentModel : null })
}
}
Widgets.AcrylicBackground {
tintColor: theme.bg.primary
alternativeColor: theme.bg.secondary
anchors.fill: parent
}
MouseArea {
// don't tranfer mouse to underlying components (#26274)
anchors.fill: parent
hoverEnabled: true
preventStealing: true
}
Item {
id: pLBannerSources
property alias model: globalMenuGroup.model
anchors.fill: parent
Column {
id: col
anchors {
fill: parent
topMargin: VLCStyle.applicationVerticalMargin
}
Item {
id: globalToolbar
width: parent.width
height: VLCStyle.globalToolbar_height
+ (menubar.visible ? menubar.height : 0)
anchors.rightMargin: VLCStyle.applicationHorizontalMargin
property bool colapseTabButtons: globalToolbar.width > (Math.max(globalToolbarLeft.width, globalToolbarRight.width) + VLCStyle.applicationHorizontalMargin)* 2
+ globalMenuGroup.model.count * VLCStyle.bannerTabButton_width_large
//drag and dbl click the titlebar in CSD mode
Loader {
anchors.fill: parent
active: root._showCSD
source: "qrc:///widgets/CSDTitlebarTapNDrapHandler.qml"
}
Column {
anchors.fill: parent
anchors.leftMargin: VLCStyle.applicationHorizontalMargin
anchors.rightMargin: VLCStyle.applicationHorizontalMargin
Menus.Menubar {
id: menubar
width: parent.width
height: implicitHeight
visible: MainCtx.hasToolbarMenu
}
Item {
width: parent.width
height: VLCStyle.globalToolbar_height
RowLayout {
id: globalToolbarLeft
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: VLCStyle.margin_xsmall
spacing: VLCStyle.margin_xxxsmall
Widgets.IconToolButton {
id: history_back
size: VLCStyle.icon_banner
iconText: VLCIcons.back
text: I18n.qtr("Previous")
height: VLCStyle.bannerButton_height
width: VLCStyle.bannerButton_width
onClicked: History.previous()
enabled: !History.previousEmpty
Navigation.parentItem: root
Navigation.rightItem: globalMenuGroup
Navigation.downItem: localMenuGroup.visible ? localMenuGroup : localToolbarBg
}
Widgets.BannerCone {
color: theme.accent
}
}
/* Button for the sources */
Widgets.NavigableRow {
id: globalMenuGroup
anchors {
top: parent.top
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
focus: true
indexFocus: root.selectedIndex
Navigation.parentItem: root
Navigation.leftItem: history_back.enabled ? history_back : null
Navigation.downItem: localMenuGroup.visible ? localMenuGroup : playlistGroup
delegate: Widgets.BannerTabButton {
iconTxt: model.icon
showText: globalToolbar.colapseTabButtons
selected: model.index === root.selectedIndex
onClicked: root.itemClicked(model.index)
height: globalMenuGroup.height
}
}
}
}
Loader {
id: globalToolbarRight
anchors {
top: parent.top
right: parent.right
rightMargin: VLCStyle.applicationHorizontalMargin
}
height: VLCStyle.globalToolbar_height
active: root._showCSD
source: VLCStyle.palette.hasCSDImage
? "qrc:///widgets/CSDThemeButtonSet.qml"
: "qrc:///widgets/CSDWindowButtonSet.qml"
}
}
//use a raw control, we don't want platforms customisation
T.Control {
id: localToolbar
width: parent.width
height: VLCStyle.localToolbar_height
onActiveFocusChanged: {
if (activeFocus) {
// sometimes when view changes, one of the "focusable" object will become disabled
// but because of focus chainning, FocusScope still tries to force active focus on the object
// but that will fail, manually assign focus in such cases
var focusable = [localContextGroup, localMenuGroup, playlistGroup]
if (!focusable.some(function (obj) { return obj.activeFocus; })) {
// no object has focus
localToolbar.nextItemInFocusChain(true).forceActiveFocus()
}
}
}
background: Rectangle {
id: localToolbarBg
color: theme.bg.secondary
Rectangle {
anchors.left : parent.left
anchors.right: parent.right
anchors.top : parent.bottom
height: VLCStyle.border
color: theme.border
}
}
contentItem: Item {
id: localToolbarContent
Widgets.NavigableRow {
id: localContextGroup
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: VLCStyle.applicationHorizontalMargin + VLCStyle.margin_xsmall
}
enabled: list_grid_btn.visible || sortControl.visible
model: ObjectModel {
id: localContextModel
property int countExtra: 0
Widgets.IconToolButton {
id: list_grid_btn
width: VLCStyle.bannerButton_width
height: VLCStyle.bannerButton_height
size: VLCStyle.icon_banner
iconText: MainCtx.gridView ? VLCIcons.list : VLCIcons.grid
text: I18n.qtr("List/Grid")
onClicked: MainCtx.gridView = !MainCtx.gridView
enabled: true
}
Widgets.SortControl {
id: sortControl
width: VLCStyle.bannerButton_width
height: VLCStyle.bannerButton_height
iconSize: VLCStyle.icon_banner
visible: root.sortModel !== undefined && root.sortModel.length > 1
enabled: visible
textRole: "text"
criteriaRole: "criteria"
sortKey: contentModel ? contentModel.sortCriteria
: PlaylistControllerModel.SORT_KEY_NONE
sortOrder: contentModel ? contentModel.sortOrder : undefined
onSortSelected: {
if (contentModel !== undefined)
contentModel.sortCriteria = key
}
onSortOrderSelected: {
if (contentModel !== undefined)
contentModel.sortOrder = type
}
}
}
Connections {
target: root
onExtraLocalActionsChanged : {
for (var i = 0; i < localContextModel.countExtra; i++) {
localContextModel.remove(localContextModel.count - localContextModel.countExtra, localContextModel.countExtra)
}
if (root.extraLocalActions && root.extraLocalActions instanceof ObjectModel) {
for (i = 0; i < root.extraLocalActions.count; i++)
localContextModel.append(root.extraLocalActions.get(i))
localContextModel.countExtra = root.extraLocalActions.count
} else {
localContextModel.countExtra = 0
}
}
}
Navigation.parentItem: root
Navigation.rightItem: localMenuGroup.visible ? localMenuGroup : playlistGroup
Navigation.upItem: globalMenuGroup
}
Flickable {
id: localMenuView
readonly property int availableWidth: parent.width
- (localContextGroup.width + playlistGroup.width)
- (VLCStyle.applicationHorizontalMargin * 2)
- (VLCStyle.margin_xsmall * 2)
- (VLCStyle.margin_xxsmall * 2)
readonly property bool _alignHCenter: ((localToolbarContent.width - contentWidth) / 2) + contentWidth < playlistGroup.x
width: Math.min(contentWidth, availableWidth)
height: VLCStyle.localToolbar_height
clip: contentWidth > width
contentWidth: localMenuGroup.width
contentHeight: VLCStyle.localToolbar_height // don't allow vertical flickering
anchors.right: playlistGroup.left
anchors.rightMargin: VLCStyle.margin_xxsmall // only applied when right aligned
ScrollBar.horizontal: ScrollBar { }
on_AlignHCenterChanged: {
if (_alignHCenter) {
anchors.horizontalCenter = localToolbarContent.horizontalCenter
anchors.right = undefined
} else {
anchors.horizontalCenter = undefined
anchors.right = playlistGroup.left
}
}
Loader {
id: localMenuGroup
focus: true
visible: !!item
enabled: status === Loader.Ready
y: status === Loader.Ready ? (VLCStyle.localToolbar_height - item.height) / 2 : 0
width: !!item
? Helpers.clamp(localMenuView.availableWidth,
localMenuGroup.item.minimumWidth || localMenuGroup.item.implicitWidth,
localMenuGroup.item.maximumWidth || localMenuGroup.item.implicitWidth)
: 0
onVisibleChanged: {
//reset the focus on the global group when the local group is hidden,
//this avoids losing the focus if the subview changes
// FIXME: This block needs refactor for keyboard focus.
if (!visible && localMenuGroup.focus) {
localMenuGroup.focus = false
globalMenuGroup.focus = true
}
}
onItemChanged: {
if (!item)
return
item.Navigation.parentItem = root
item.Navigation.leftItem = Qt.binding(function(){ return localContextGroup.enabled ? localContextGroup : null})
item.Navigation.rightItem = Qt.binding(function(){ return playlistGroup.enabled ? playlistGroup : null})
item.Navigation.upItem = globalMenuGroup
}
}
}
Widgets.NavigableRow {
id: playlistGroup
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: VLCStyle.applicationHorizontalMargin + VLCStyle.margin_xsmall
}
spacing: VLCStyle.margin_xxxsmall
model: ObjectModel {
Widgets.SearchBox {
id: searchBox
visible: !!root.contentModel
height: VLCStyle.bannerButton_height
buttonWidth: VLCStyle.bannerButton_width
}
Widgets.IconToolButton {
id: playlist_btn
checked: MainCtx.playlistVisible
size: VLCStyle.icon_banner
iconText: VLCIcons.playlist
text: I18n.qtr("Playlist")
width: VLCStyle.bannerButton_width
height: VLCStyle.bannerButton_height
highlighted: MainCtx.playlistVisible
onClicked: MainCtx.playlistVisible = !MainCtx.playlistVisible
DropArea {
anchors.fill: parent
onContainsDragChanged: {
if (containsDrag) {
_timer.restart()
if (plListView)
MainCtx.setCursor(Qt.DragCopyCursor)
} else {
_timer.stop()
if (plListView)
MainCtx.restoreCursor()
}
}
onEntered: {
if (drag.hasUrls || Helpers.isValidInstanceOf(drag.source, Widgets.DragItem)) {
drag.accept() // Not actually necessary, as it is accepted by default
} else {
drag.accepted = false
}
}
onDropped: {
if (plListView)
plListView.acceptDrop(plListView.model.count, drop)
}
Timer {
id: _timer
interval: VLCStyle.duration_humanMoment
onTriggered: {
MainCtx.playlistVisible = true
}
}
}
}
Widgets.IconToolButton {
id: menu_selector
visible: !MainCtx.hasToolbarMenu
size: VLCStyle.icon_banner
iconText: VLCIcons.menu
text: I18n.qtr("Menu")
width: VLCStyle.bannerButton_width
height: VLCStyle.bannerButton_height
onClicked: contextMenu.popup(this.mapToGlobal(0, height))
QmlGlobalMenu {
id: contextMenu
ctx: MainCtx
}
}
}
Navigation.parentItem: root
Navigation.leftItem: localMenuGroup.visible ? localMenuGroup : localContextGroup
Navigation.upItem: globalMenuGroup
}
}
}
}
Keys.priority: Keys.AfterItem
Keys.onPressed: root.Navigation.defaultKeyAction(event)
}
}