Warning, /graphics/spectacle/src/Gui/ImageView.qml is written in an unsupported language. File is not indexed.

0001 /* SPDX-FileCopyrightText: 2022 Noah Davis <noahadvs@gmail.com>
0002  * SPDX-License-Identifier: LGPL-2.0-or-later
0003  */
0004 
0005 import QtQuick
0006 import QtQuick.Window
0007 import QtQuick.Layouts
0008 import QtQuick.Controls as QQC
0009 import org.kde.kirigami as Kirigami
0010 import org.kde.spectacle.private
0011 
0012 import "Annotations"
0013 
0014 /**
0015  * This page is shown when a screenshot has been taken
0016  * or accepted from the rectangular region capture mode.
0017  *
0018  * - There is a `contextWindow` context property that can be used to
0019  * access the instance of the ViewerWindow.
0020  */
0021 EmptyPage {
0022     id: root
0023     focus: true
0024 
0025     // Used in ViewerWindow::setMode()
0026     readonly property real minimumWidth: Math.max(
0027         header.implicitWidth,
0028         annotationsToolBar.implicitWidth + separator.implicitWidth + footerLoader.implicitWidth,
0029         captureOptionsLoader.implicitWidth + 480 // leave some room for content if necessary
0030     )
0031     readonly property real minimumHeight: header.implicitHeight
0032         + Math.max(annotationsToolBar.implicitHeight,
0033                    footerLoader.implicitHeight,
0034                    captureOptionsLoader.implicitHeight)
0035 
0036     property var inlineMessageData: {}
0037     property string inlineMessageSource: ""
0038     onInlineMessageDataChanged: {
0039         if (inlineMessageSource) {
0040             inlineMessageLoader.setSource(inlineMessageSource, inlineMessageData)
0041             inlineMessageLoader.state = "active"
0042         }
0043     }
0044 
0045     LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
0046     LayoutMirroring.childrenInherit: true
0047 
0048     header: QQC.ToolBar {
0049         id: header
0050         contentItem: MainToolBarContents {
0051             id: mainToolBarContents
0052             showNewScreenshotButton: false
0053             showOptionsMenu: false
0054             showUndoRedo: contextWindow.annotating
0055             displayMode: QQC.AbstractButton.TextBesideIcon
0056         }
0057     }
0058 
0059     // Needed for scrolling via keyboard input
0060     Keys.priority: Keys.AfterItem
0061     Keys.enabled: !SpectacleCore.videoMode && contentLoader.item !== null
0062     Keys.forwardTo: contentLoader.item
0063 
0064     AnimatedLoader { // parent is contentItem
0065         id: inlineMessageLoader
0066         anchors.left: annotationsToolBar.right
0067         anchors.right: captureOptionsLoader.left
0068         anchors.top: parent.top
0069         anchors.margins: visible ? Kirigami.Units.mediumSpacing : 0
0070         state: "inactive"
0071         height: visible ? implicitHeight : 0
0072         Behavior on height {
0073             NumberAnimation {
0074                 duration: inlineMessageLoader.animationDuration
0075                 easing.type: Easing.OutCubic
0076             }
0077         }
0078     }
0079 
0080     QQC.Pane { // parent is contentItem
0081         id: annotationsToolBar
0082         anchors.top: parent.top
0083         anchors.bottom: parent.bottom
0084         anchors.right: parent.left
0085         visible: false
0086         leftPadding: header.leftPadding
0087         rightPadding: header.rightPadding
0088         topPadding: header.topPadding
0089         bottomPadding: header.bottomPadding
0090         contentItem: AnnotationsToolBarContents {
0091             id: annotationsToolBarContents
0092             displayMode: QQC.AbstractButton.IconOnly
0093             flow: Grid.TopToBottom
0094             showUndoRedo: false
0095             rememberToolType: true
0096         }
0097         background: Rectangle {
0098             color: parent.palette.window
0099         }
0100     }
0101 
0102     Kirigami.Separator { // parent is contentItem
0103         id: separator
0104         visible: false
0105         anchors.left: annotationsToolBar.right
0106         anchors.top: parent.top
0107         anchors.bottom: parent.bottom
0108     }
0109 
0110     Loader { // parent is contentItem
0111         id: contentLoader
0112         anchors {
0113             left: footerLoader.left
0114             right: captureOptionsLoader.left
0115             top: inlineMessageLoader.bottom
0116             bottom: footerLoader.top
0117             topMargin: inlineMessageLoader.active ? Kirigami.Units.mediumSpacing : 0
0118         }
0119         source: SpectacleCore.videoMode ? "RecordingView.qml" : "ScreenshotView.qml"
0120     }
0121 
0122     Loader { // parent is contentItem
0123         id: captureOptionsLoader
0124         visible: true
0125         active: visible
0126         anchors {
0127             top: parent.top
0128             bottom: parent.bottom
0129             right: parent.right
0130         }
0131         width: Math.max(implicitWidth, Kirigami.Units.gridUnit * 15)
0132         sourceComponent: QQC.Page {
0133 
0134             leftPadding: Kirigami.Units.mediumSpacing * 2
0135                 + (!mirrored ? sideBarSeparator.implicitWidth : 0)
0136             rightPadding: Kirigami.Units.mediumSpacing * 2
0137                 + (mirrored ? sideBarSeparator.implicitWidth : 0)
0138             topPadding: Kirigami.Units.mediumSpacing * 2
0139             bottomPadding: Kirigami.Units.mediumSpacing * 2
0140 
0141             header: QQC.TabBar {
0142                 id: tabBar
0143                 visible: VideoPlatform.supportedRecordingModes
0144                 currentIndex: 0
0145                 QQC.TabButton {
0146                     width: tabBar.width / tabBar.count
0147                     text: i18n("Screenshot")
0148                 }
0149                 QQC.TabButton {
0150                     width: tabBar.width / tabBar.count
0151                     text: i18n("Recording")
0152                 }
0153             }
0154 
0155             contentItem: Loader {
0156                 source: switch (tabBar.currentIndex) {
0157                     case 0: return "CaptureOptions.qml"
0158                     case 1: return "RecordOptions.qml"
0159                     default: return ""
0160                 }
0161             }
0162 
0163             background: Rectangle {
0164                 color: Kirigami.Theme.backgroundColor
0165                 Kirigami.Separator {
0166                     id: sideBarSeparator
0167                     anchors {
0168                         left: parent.left
0169                         top: parent.top
0170                         bottom: parent.bottom
0171                     }
0172                 }
0173             }
0174         }
0175     }
0176 
0177     Loader {
0178         id: footerLoader
0179         anchors.left: separator.right
0180         anchors.right: captureOptionsLoader.left
0181         anchors.top: parent.bottom
0182         visible: false
0183         active: visible
0184         sourceComponent: QQC.ToolBar { // parent is contentItem
0185             position: QQC.ToolBar.Footer
0186             contentHeight: QmlUtils.iconTextButtonHeight
0187             contentItem: RowLayout {
0188                 spacing: Kirigami.Units.mediumSpacing
0189                 AnimatedLoader {
0190                     id: loader
0191                     Layout.fillWidth: true
0192                     active: opacity > 0
0193                     visible: true
0194                     state: if (AnnotationDocument.tool.options !== AnnotationTool.NoOptions
0195                         || (AnnotationDocument.tool.type === AnnotationTool.SelectTool
0196                             && AnnotationDocument.selectedItem.options !== AnnotationTool.NoOptions)
0197                     ) {
0198                         return "active"
0199                     } else {
0200                         return "inactive"
0201                     }
0202                     source: "AnnotationOptionsToolBarContents.qml"
0203                 }
0204                 QQC.ToolSeparator {
0205                     Layout.fillHeight: true
0206                     visible: loader.implicitWidth
0207                         + implicitWidth
0208                         + zoomLabel.implicitWidth
0209                         + zoomEditor.implicitWidth
0210                         + parent.spacing * 3 >= parent.width
0211                 }
0212                 QQC.Label {
0213                     id: zoomLabel
0214                     text: i18n("Zoom:")
0215                 }
0216                 QQC.SpinBox {
0217                     id: zoomEditor
0218                     from: contentLoader.item.minZoom * 100
0219                     to: contentLoader.item.maxZoom * 100
0220                     stepSize: 25
0221                     value: contentLoader.item.effectiveZoom * 100
0222                     textFromValue: (value, locale) => {
0223                         return Number(Math.round(value)).toLocaleString(locale, 'f', 0) + locale.percent
0224                     }
0225                     valueFromText: (text, locale) => {
0226                         return Number.fromLocaleString(locale, text.replace(/\D/g,''))
0227                     }
0228                     QQC.ToolTip.text: i18n("Image Zoom")
0229                     QQC.ToolTip.visible: hovered
0230                     QQC.ToolTip.delay: Kirigami.Units.toolTipDelay
0231                     Binding {
0232                         target: zoomEditor.contentItem
0233                         property: "horizontalAlignment"
0234                         value: Text.AlignRight
0235                         restoreMode: Binding.RestoreNone
0236                     }
0237                     onValueModified: contentLoader.item.zoomToPercent(Math.round(value) / 100)
0238                 }
0239             }
0240         }
0241     }
0242 
0243     Shortcut {
0244         enabled: contextWindow.annotating && !SpectacleCore.videoMode && contentLoader.item !== null
0245         sequences: [StandardKey.ZoomIn]
0246         onActivated: contentLoader.item.zoomIn()
0247     }
0248     Shortcut {
0249         enabled: contextWindow.annotating && !SpectacleCore.videoMode && contentLoader.item !== null
0250         sequences: [StandardKey.ZoomOut]
0251         onActivated: contentLoader.item.zoomOut()
0252     }
0253     // FIXME: This shortcut only exists here because spectacle interprets "Ctrl+Shift+,"
0254     // as "Ctrl+Shift+<" for some reason unless we use a QML Shortcut.
0255     Shortcut {
0256         sequences: [StandardKey.Preferences]
0257         onActivated: contextWindow.showPreferencesDialog()
0258     }
0259 
0260     state: "normal"
0261     states: [
0262         State {
0263             name: "annotating"
0264             when: contextWindow.annotating
0265                 && !SpectacleCore.videoMode
0266                 && contentLoader.item !== null
0267             AnchorChanges {
0268                 target: annotationsToolBar
0269                 anchors.left: parent.left
0270                 anchors.right: undefined
0271             }
0272             AnchorChanges {
0273                 target: footerLoader
0274                 anchors.bottom: parent.bottom
0275                 anchors.top: undefined
0276             }
0277             AnchorChanges {
0278                 target: captureOptionsLoader
0279                 anchors.left: parent.right
0280                 anchors.right: undefined
0281             }
0282         },
0283         State {
0284             name: "normal"
0285             when: !contextWindow.annotating
0286                 || SpectacleCore.videoMode
0287                 || contentLoader.item === null
0288             AnchorChanges {
0289                 target: annotationsToolBar
0290                 anchors.left: undefined
0291                 anchors.right: parent.left
0292             }
0293             AnchorChanges {
0294                 target: footerLoader
0295                 anchors.bottom: undefined
0296                 anchors.top: parent.bottom
0297             }
0298             AnchorChanges {
0299                 target: captureOptionsLoader
0300                 anchors.left: undefined
0301                 anchors.right: parent.right
0302             }
0303         }
0304     ]
0305     transitions: [
0306         Transition {
0307             to: "annotating"
0308             SequentialAnimation {
0309                 PropertyAction {
0310                     targets: [annotationsToolBar, separator, footerLoader]
0311                     property: "visible"
0312                     value: true
0313                 }
0314                 AnchorAnimation {
0315                     duration: Kirigami.Units.longDuration
0316                     easing.type: Easing.OutCubic
0317                 }
0318                 PropertyAction {
0319                     targets: captureOptionsLoader
0320                     property: "visible"
0321                     value: false
0322                 }
0323             }
0324         },
0325         Transition {
0326             to: "normal"
0327             SequentialAnimation {
0328                 PropertyAction {
0329                     targets: captureOptionsLoader
0330                     property: "visible"
0331                     value: true
0332                 }
0333                 AnchorAnimation {
0334                     duration: Kirigami.Units.longDuration
0335                     easing.type: Easing.OutCubic
0336                 }
0337                 PropertyAction {
0338                     targets: [annotationsToolBar, separator, footerLoader]
0339                     property: "visible"
0340                     value: false
0341                 }
0342             }
0343         }
0344     ]
0345 }