1/* 2 * Copyright (c) 2014-2021 Meltytech, LLC 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18import QtQuick 2.1 19import QtQuick.Controls 2.12 20import QtQuick.Layouts 1.0 21import Shotcut.Controls 1.0 as Shotcut 22import org.shotcut.qml 1.0 as Shotcut 23 24Rectangle { 25 id: root 26 property int selectedIndex: Shotcut.Filter.NoCurrentFilter 27 signal currentFilterRequested(int attachedIndex) 28 29 function clearCurrentFilter() { 30 if (filterConfig.item) { 31 filterConfig.item.width = 1 32 filterConfig.item.height = 1 33 } 34 filterConfig.source = "" 35 } 36 37 function setCurrentFilter(index) { 38 clearCurrentFilter() 39 attachedFilters.setCurrentFilter(index) 40 selectedIndex = index 41 filterConfig.source = metadata ? metadata.qmlFilePath : "" 42 } 43 44 function openFilterMenu() { 45 if (attachedfiltersmodel.isProducerSelected) 46 filterMenu.open() 47 } 48 49 color: activePalette.window 50 width: 400 51 52 onWidthChanged: _setLayout() 53 onHeightChanged: _setLayout() 54 55 function _setLayout() { 56 if (height > width - attachedFilters.minimumWidth) { 57 root.state = "portrait" 58 } else { 59 root.state = "landscape" 60 } 61 } 62 63 SystemPalette { id: activePalette } 64 65 Rectangle { 66 id: titleBackground 67 anchors { 68 top: parent.top 69 left: parent.left 70 right: parent.right 71 bottom: titleLabel.bottom 72 topMargin: 10 73 leftMargin: 10 74 rightMargin: 10 75 } 76 color: activePalette.highlight 77 visible: attachedfiltersmodel.producerTitle != "" 78 } 79 80 Label { 81 id: titleLabel 82 anchors { 83 top: parent.top 84 left: parent.left 85 right: parent.right 86 topMargin: 10 87 leftMargin: 10 88 rightMargin: 10 89 } 90 text: attachedfiltersmodel.producerTitle 91 elide: Text.ElideLeft 92 color: activePalette.highlightedText 93 font.bold: true 94 horizontalAlignment: Text.AlignHCenter 95 } 96 97 98 GridLayout { 99 id: attachedContainer 100 columns: children.length - 1 101 anchors { 102 top: titleBackground.bottom 103 left: parent.left 104 leftMargin: 10 105 rightMargin: 10 106 topMargin: 6 107 bottomMargin: 4 108 } 109 110 AttachedFilters { 111 id: attachedFilters 112 property int minimumWidth: application.OS === 'Windows'? 350 : 250 113 Layout.columnSpan: parent.columns 114 Layout.fillWidth: true 115 Layout.fillHeight: true 116 onFilterClicked: { 117 root.currentFilterRequested(index) 118 } 119 Label { 120 anchors.centerIn: parent 121 text: qsTr("Nothing selected") 122 color: activePalette.text 123 visible: !attachedfiltersmodel.isProducerSelected 124 } 125 } 126 127 Shotcut.Button { 128 id: addButton 129 implicitWidth: height 130 icon.name: 'list-add' 131 icon.source: 'qrc:///icons/oxygen/32x32/actions/list-add.png' 132 enabled: attachedfiltersmodel.isProducerSelected 133 opacity: enabled ? 1.0 : 0.5 134 Shotcut.HoverTip { text: qsTr('Add a filter') } 135 onClicked: { 136 if (application.confirmOutputFilter()) { 137 filterMenu.open() 138 } 139 } 140 } 141 Shotcut.Button { 142 id: removeButton 143 implicitWidth: height 144 icon.name: 'list-remove' 145 icon.source: 'qrc:///icons/oxygen/32x32/actions/list-remove.png' 146 enabled: selectedIndex > Shotcut.Filter.NoCurrentFilter 147 opacity: enabled ? 1.0 : 0.5 148 Shotcut.HoverTip { text: qsTr('Remove selected filter') } 149 onClicked: { 150 attachedfiltersmodel.remove(selectedIndex) 151 } 152 } 153 Shotcut.Button { // separator 154 enabled: false 155 implicitWidth: 1 156 implicitHeight: 20 157 } 158 Shotcut.Button { 159 id: copyButton 160 implicitWidth: height 161 icon.name: 'edit-copy' 162 icon.source: 'qrc:///icons/oxygen/32x32/actions/edit-copy.png' 163 enabled: selectedIndex > Shotcut.Filter.NoCurrentFilter 164 opacity: enabled ? 1.0 : 0.5 165 Shotcut.HoverTip { text: qsTr('Copy the filters') } 166 onClicked: application.copyFilters() 167 } 168 Shotcut.Button { 169 id: pasteButton 170 implicitWidth: height 171 enabled: application.hasFiltersOnClipboard && attachedfiltersmodel.isProducerSelected 172 opacity: enabled ? 1.0 : 0.5 173 icon.name: 'edit-paste' 174 icon.source: 'qrc:///icons/oxygen/32x32/actions/edit-paste.png' 175 Shotcut.HoverTip { text: qsTr('Paste filters') } 176 onClicked: application.pasteFilters() 177 } 178 Shotcut.Button { // separator 179 enabled: false 180 implicitWidth: 1 181 implicitHeight: 20 182 } 183 Shotcut.Button { 184 id: moveUpButton 185 implicitWidth: height 186 enabled: selectedIndex > 0 187 opacity: enabled ? 1.0 : 0.5 188 icon.name: 'lift' 189 icon.source: 'qrc:///icons/oxygen/32x32/actions/lift.png' 190 Shotcut.HoverTip { text: qsTr('Move filter up') } 191 onClicked: attachedfiltersmodel.move(selectedIndex, --selectedIndex) 192 } 193 Shotcut.Button { 194 id: moveDownButton 195 implicitWidth: height 196 enabled: selectedIndex > Shotcut.Filter.NoCurrentFilter && selectedIndex + 1 < attachedfiltersmodel.rowCount() 197 opacity: enabled ? 1.0 : 0.5 198 icon.name: 'overwrite' 199 icon.source: 'qrc:///icons/oxygen/32x32/actions/overwrite.png' 200 Shotcut.HoverTip { text: qsTr('Move filter down') } 201 onClicked: attachedfiltersmodel.move(selectedIndex, ++selectedIndex) 202 } 203 Shotcut.Button { // separator 204 enabled: false 205 implicitWidth: 1 206 implicitHeight: 20 207 } 208 Shotcut.Button { 209 id: deselectButton 210 implicitWidth: height 211 icon.name: 'window-close' 212 icon.source: 'qrc:///icons/oxygen/32x32/actions/window-close.png' 213 enabled: selectedIndex > Shotcut.Filter.NoCurrentFilter 214 opacity: enabled ? 1.0 : 0.5 215 Shotcut.HoverTip { text: qsTr('Deselect the filter') } 216 onClicked: { 217 clearCurrentFilter() 218 attachedFilters.setCurrentFilter(Shotcut.Filter.DeselectCurrentFilter) 219 selectedIndex = Shotcut.Filter.NoCurrentFilter 220 filter.deselect() 221 } 222 } 223 Item { 224 Layout.fillWidth: true 225 } 226 } 227 228 Flickable { 229 id: filterConfigScrollView 230 clip: true 231 interactive: false 232 anchors.bottomMargin: 16 233 anchors.rightMargin: 16 234 contentWidth: filterConfig.item.width + 16 235 contentHeight: filterConfig.item.height + 16 236 ScrollBar.horizontal: ScrollBar { 237 height: 16 238 policy: ScrollBar.AlwaysOn 239 visible: filterConfigScrollView.contentWidth > filterConfigScrollView.width 240 parent: filterConfigScrollView.parent 241 anchors.top: filterConfigScrollView.bottom 242 anchors.left: filterConfigScrollView.left 243 anchors.right: filterConfigScrollView.right 244 background: Rectangle { color: parent.palette.alternateBase } 245 } 246 ScrollBar.vertical: ScrollBar { 247 width: 16 248 policy: ScrollBar.AlwaysOn 249 visible: filterConfigScrollView.contentHeight > filterConfigScrollView.height 250 parent: filterConfigScrollView.parent 251 anchors.top: filterConfigScrollView.top 252 anchors.left: filterConfigScrollView.right 253 anchors.bottom: filterConfigScrollView.bottom 254 background: Rectangle { color: parent.palette.alternateBase } 255 } 256 257 function expandWidth() { 258 if (filterConfig.item) { 259 filterConfig.item.width = 260 Math.max(filterConfig.minimumWidth, 261 filterConfigScrollView.width - 20 /* scroll bar */) 262 } 263 } 264 onWidthChanged: expandWidth() 265 Loader { 266 id: filterConfig 267 enabled: !filterMenu.visible 268 property int minimumWidth: 0 269 onLoaded: { 270 minimumWidth = item.width 271 filterConfigScrollView.expandWidth() 272 } 273 } 274 } 275 276 FilterMenu { 277 id: filterMenu 278 anchors { 279 top: titleBackground.bottom 280 left: parent.left 281 right: parent.right 282 bottom: parent.bottom 283 topMargin: attachedContainer.anchors.topMargin 284 } 285 z: 1 286 onFilterSelected: { 287 attachedfiltersmodel.add(metadatamodel.get(index)) 288 } 289 } 290 291 states: [ 292 State { 293 name: "landscape" 294 AnchorChanges { 295 target: filterConfigScrollView 296 anchors { 297 top: titleBackground.bottom 298 bottom: root.bottom 299 left: attachedContainer.right 300 right: root.right 301 } 302 } 303 PropertyChanges { 304 target: attachedContainer 305 width: attachedFilters.minimumWidth 306 height: root.height - 307 titleBackground.height - titleBackground.anchors.topMargin - titleBackground.anchors.bottomMargin - 308 attachedContainer.anchors.topMargin - attachedContainer.anchors.bottomMargin 309 } 310 }, 311 State { 312 name: "portrait" 313 AnchorChanges { 314 target: filterConfigScrollView 315 anchors { 316 top: attachedContainer.bottom 317 bottom: root.bottom 318 left: root.left 319 right: root.right 320 } 321 } 322 PropertyChanges { 323 target: attachedContainer 324 width: titleBackground.width 325 height: 165 326 } 327 } 328 ] 329 330 Connections { 331 target: attachedfiltersmodel 332 function onIsProducerSelectedChanged() { 333 filterMenu.close() 334 } 335 } 336} 337