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 }