Коммит ce593f45 создал по автору vladislav.larionov's avatar vladislav.larionov
Просмотр файлов

Add components from docviewer

владелец 940b0b6b
/*
* Copyright (c) 2020 - 2023 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.3
import Sailfish.Silica 1.0
import Sailfish.Silica.private 1.0
import "components"
Item {
id: root
property int minimumValue
property int maximumValue
property int currentValue
property var pageNames
property bool tabsNavigation
property bool selectionMode
property string url
signal currentPageChanged(var newPageNumber)
signal copySelectionText
signal shareSelectionText
signal clearSelectionText
height: content.height
MouseArea {
// Trapping mouse events on an element
anchors.fill: parent
}
Rectangle {
id: navigationToolbar
color: Qt.tint(palette.colorScheme === Theme.LightOnDark ? "black" : "white",
Theme.rgba(palette.primaryColor, Theme.opacityFaint))
anchors.fill: parent
Loader {
id: content
width: parent.width
sourceComponent: url ? urlToolbar : selectionMode ? slectionToolbar : tabsNavigation ? tabsToolbar : sliderToolbar
}
}
Component {
id: sliderToolbar
Item {
height: slider.height + Theme.paddingLarge * 2
Slider {
id: slider
property bool blockSignals
onDownChanged: {
if (blockSignals)
return
if (!down) {
currentPageShowTimer.stop()
currentPageIndex.opacity = 0.0
currentPageChanged(value)
} else {
currentPageShowTimer.start()
}
}
stepSize: 1
valueText: ""
minimumValue: root.minimumValue
maximumValue: root.maximumValue
value: root.currentValue
secondaryHighlightColor: color
animateValue: false
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
anchors {
left: parent.left
leftMargin: Theme.horizontalPageMargin
right: parent.right
rightMargin: Theme.horizontalPageMargin
verticalCenter: parent.verticalCenter
}
Text {
id: currentPageIndex
x: slider._highlightX + slider._highlightPadding + (slider.colorScheme === Theme.LightOnDark ? 1.5 : 1) * 2 * slider._glassItemPadding - width * 0.5
y: slider._progressBarItem.y - height
opacity: 0.0
color: palette.primaryColor
text: slider.value
font.pixelSize: Theme.fontSizeMedium
Behavior on opacity {
NumberAnimation {
duration: 200
}
}
}
Text {
color: palette.secondaryColor
text: "/"
anchors { top: slider.bottom; topMargin: -height * 0.75; horizontalCenter: parent.horizontalCenter }
font.pixelSize: Theme.fontSizeMedium
Text {
color: palette.primaryColor
text: slider.value
anchors.right: parent.left
font.pixelSize: parent.font.pixelSize
}
Text {
color: parent.color
text: slider.maximumValue
font.pixelSize: parent.font.pixelSize
anchors.left: parent.right
}
}
Timer {
id: currentPageShowTimer
onTriggered: currentPageIndex.opacity = 1.0
interval: 200
}
}
Connections {
target: root
onCurrentValueChanged: {
slider.blockSignals = true
slider.value = root.currentValue
slider.blockSignals = false
}
}
}
}
Component {
id: tabsToolbar
TabView {
id: tabView
property var pageNames: root.pageNames
property bool blockSignals
onPageNamesChanged: {
namesModel.clear()
for (var i in pageNames) namesModel.append({"title": pageNames[i]})
}
onCurrentIndexChanged: if (!blockSignals) currentPageChanged(currentIndex + 1)
model: root.maximumValue
delegate: Item {}
footer: TabBar {
id: tabBar
model: namesModel
delegate: TabButton {
title: model.title
Rectangle {
anchors.fill: parent
color: navigationToolbar.color
z: -1
}
}
Component.onCompleted: tabView.height = Qt.binding(function() { return height } )
}
ListModel {
id: namesModel
}
Connections {
target: root
onCurrentValueChanged: {
tabView.blockSignals = true
if (tabsNavigation) tabView.moveTo(root.currentValue - 1)
tabView.blockSignals = false
}
}
}
}
Component {
id: urlToolbar
Item {
height: linkedLabel.height + Theme.paddingLarge * 2
LinkedLabel {
id: linkedLabel
anchors.centerIn: parent
x: Theme.horizontalPageMargin
width: parent.width - 2*x
visible: root.url
plainText: root.url
color: highlighted ? Theme.highlightColor : Theme.primaryColor
linkColor: Theme.highlightColor
shortenUrl: true
horizontalAlignment: lineCount > 1 ? Text.AlignLeft : Text.AlignHCenter
}
}
}
Component {
id: slectionToolbar
TextSelectionToolbar {
onShare: shareSelectionText()
onCopy: copySelectionText()
onClear: clearSelectionText()
}
}
}
/*
* Copyright (с) 2021 - 2022 Open Mobile Platform LLC.
*
* 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; version 2 only.
*
* 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.6
import Sailfish.Silica 1.0
BackgroundItem {
id: root
property alias text: label.text
property alias iconSource: icon.source
property alias checked: toggle.checked
property alias horizontalAlignmentText: label.horizontalAlignment
property bool checkable
property int horizontalOffset
property int iconWidth
width: parent.width
Item {
id: iconContainer
width: iconWidth
height: parent.height
x: Theme.paddingMedium
Icon {
id: icon
anchors {
centerIn: parent
horizontalCenterOffset: horizontalOffset
}
highlighted: root.highlighted
color: Theme.primaryColor
opacity: root.enabled ? 1.0 : Theme.opacityLow
}
}
Label {
id: label
anchors {
left: iconContainer.right
leftMargin: Theme.paddingMedium
right: parent.right
rightMargin: toggle.visible ? toggle.width + Theme.paddingMedium : Theme.horizontalPageMargin
verticalCenter: parent.verticalCenter
}
color: root.highlighted ? Theme.highlightColor : Theme.primaryColor
opacity: root.enabled ? 1.0 : Theme.opacityLow
truncationMode: TruncationMode.Fade
Behavior on opacity { FadeAnimation {} }
}
Switch {
id: toggle
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
}
visible: checkable
onClicked: root.clicked(mouse)
}
}
/*
* Copyright (с) 2021 - 2022 Open Mobile Platform LLC.
*
* 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; version 2 only.
*
* 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.2
import Sailfish.Silica 1.0
import Sailfish.Silica.Background 1.0
import Sailfish.Silica.private 1.0 as Private
SilicaControl {
id: popUpMenu
property var menuItem
property var footer
property alias active: menuLoader.active
property bool activeBlock: true
property bool isShowFull
property int margin: Theme.paddingMedium
property int verticalPosition: Qt.AlignVCenter
property int horizontalPosition: Qt.AlignHCenter
property bool headerItemVisible
property int minHeight: Theme.paddingLarge * heightRatio
property int widthRatio: 18
readonly property int cornerRadius: 12
readonly property int heightRatio: 28
signal closed
Private.AnimatedLoader {
id: menuLoader
width: popUpMenu.width
height: popUpMenu.height
animating: menuAnimation.running
active: false
source: menuComponent
onAnimate: {
if (item) {
item.percentageClosed = 1
menuAnimation.target = item
menuAnimation.from = item.percentageClosed
menuAnimation.to = 0
menuAnimation.restart()
} else if (replacedItem) {
menuAnimation.target = replacedItem
menuAnimation.from = 0
menuAnimation.to = 1
menuAnimation.restart()
}
}
}
NumberAnimation {
id: menuAnimation
running: false
duration: 200
easing.type: Easing.InOutQuad
property: "percentageClosed"
}
Component {
id: menuComponent
Rectangle {
id: menuItem
property real percentageClosed
readonly property real menuTop: headerItem.y - menuFlickable.contentY
readonly property real topPadding: Math.max(0, footerLoader.y - headerItem.height - (Screen.sizeCategory > Screen.Medium
? contentLoader.height
: Math.min(contentLoader.height, popUpMenu.minHeight)))
width: popUpMenu.width
height: popUpMenu.height
color: activeBlock ? Theme.rgba("black", Theme.opacityLow * (1 - percentageClosed)) : "transparent"
Flickable {
id: menuFlickable
property int topPosition: popUpMenu.margin
- (menuItem.percentageClosed * (height + menuItem.menuTop + popUpMenu.margin))
property int centerPosition: popUpMenu.height / 2
- height / 2
- popUpMenu.margin
property int bottomPosition: popUpMenu.height
- height
- popUpMenu.margin
+ (menuItem.percentageClosed * (height - menuItem.menuTop + popUpMenu.margin))
x: popUpMenu.horizontalPosition === Qt.AlignRight
? popUpMenu.width - width - popUpMenu.margin
: popUpMenu.horizontalPosition === Qt.AlignLeft
? popUpMenu.margin
: popUpMenu.width / 2 - width / 2
y: popUpMenu.verticalPosition === Qt.AlignTop ? topPosition : popUpMenu.verticalPosition === Qt.AlignBottom ? bottomPosition : centerPosition
width: Math.min(
popUpMenu.width - Theme.paddingLarge * 2,
Math.max(
Theme.paddingLarge * widthRatio,
footerLoader.item ? footerLoader.item.implicitWidth : 0))
height: Math.min(
popUpMenu.height - (2 * popUpMenu.margin),
headerItem.height + contentLoader.height + footerLoader.height)
contentHeight: menuItem.topPadding + headerItem.height + contentLoader.height + footerLoader.height
interactive: popUpMenu.active // Don't handle mouse events during fade out.
flickableDirection: Flickable.VerticalFlick
boundsBehavior: Flickable.DragOverBounds
onHeightChanged: popUpMenu.isShowFull = height === contentHeight
onContentHeightChanged: popUpMenu.isShowFull = height === contentHeight
onDragEnded: {
if (popUpMenu.verticalPosition === Qt.AlignBottom && contentY < -popUpMenu.margin - Theme.paddingLarge) {
topMargin = -contentY
popUpMenu.closed()
}
if (popUpMenu.verticalPosition === Qt.AlignTop && contentY > -popUpMenu.margin - Theme.paddingLarge) {
topMargin = -contentY
popUpMenu.closed()
}
}
Item {
id: headerItem
y: menuItem.topPadding
+ Math.min(0, menuFlickable.contentY)
- Math.min(0, menuFlickable.contentHeight - menuFlickable.height - menuFlickable.contentY)
width: menuFlickable.width
height: headerItemVisible ? Theme.paddingLarge : 0 // The element is left for ease of porting, but it is not displayed
}
Private.AnimatedLoader {
id: contentLoader
y: headerItem.y + headerItem.height
width: menuFlickable.width
height: item ? item.height : 0
source: popUpMenu.menuItem
onInitializeItem: {
item.width = Qt.binding(function() { return menuFlickable.width })
}
}
children: [
Rectangle {
id: background
y: Math.max(0, headerItem.y - menuFlickable.contentY)
z: -1
width: footerLoader.width
height: footerLoader.y - y
color: Qt.tint(
popUpMenu.palette.colorScheme === Theme.LightOnDark ? "black" : "white",
Theme.rgba(popUpMenu.palette.primaryColor, Theme.opacityFaint))
},
Item {
id: decoratorParent
y: background.y + headerItem.height
width: footerLoader.width
height: footerLoader.y - y
VerticalScrollDecorator {
_forcedParent: parent
flickable: menuFlickable
_sizeRatio: decoratorParent.height / (menuFlickable.contentHeight - contentLoader.y - footerLoader.height)
y: menuFlickable.contentHeight > menuFlickable.height + headerItem.y
? ((decoratorParent.height - height)
* Math.max(0, menuFlickable.contentY - headerItem.y)
/ (menuFlickable.contentHeight - menuFlickable.height - headerItem.y))
: 0
}
},
Rectangle {
y: Math.max(0, headerItem.y - menuFlickable.contentY)
width: headerItem.width
height: headerItem.height + Theme.paddingMedium
color: background.color
Rectangle {
x: (headerItem.width - width) / 2
y: (headerItem.height - height)
width: Theme.itemSizeLarge
height: Theme.paddingSmall
radius: height / 2
color: popUpMenu.palette.primaryColor
opacity: menuFlickable.contentY < headerItem.y ? 1 : Theme.opacityLow
}
},
MouseArea {
anchors.fill: footerLoader
},
Private.AnimatedLoader {
id: footerLoader
y: menuFlickable.height - height
width: menuFlickable.width
height: item ? item.height: 0
source: popUpMenu.footer
onInitializeItem: {
item.width = Qt.binding(function() { return menuFlickable.width })
}
}
]
}
// To round the corners the menu is being rendered to a ShaderEffectSource which is
// used as the source item for a Background item which composites the menu with the
// rest of the UI with rounded corners. Using the ShaderEffectSource also means
// overlapping items in the menu won't blend together when the menu fades in and out.
ShaderEffectSource {
id: menuShaderSource
width: menuFlickable.width
height: menuFlickable.height
sourceItem: menuFlickable
hideSource: true
visible: false
}
Background {
id: menuShaderItem
// The ShaderEffectSourceItem has its size fixed to the maximum open size of the
// menu, this is so each frame of animation doesn't have to allocate a new texture
// of a different size when the menu expands. This matrix transforms the normalized
// item rectangle coordinates to the visible sub rectangle of the source item.
readonly property matrix4x4 menuSourceMatrix: Qt.matrix4x4(
1, 0, 0, 0,
0, height / menuFlickable.height, 0, 0,
0, 0, 1, 0,
0, menuItem.menuTop / menuFlickable.height, 0, 1)
x: menuFlickable.x
y: menuFlickable.y + menuItem.menuTop - (popUpMenu.verticalPosition === Qt.AlignTop
? Math.max(0, menuFlickable.contentY)
: popUpMenu.verticalPosition === Qt.AlignBottom ? Math.min(0, menuFlickable.contentY) : 0)
width: menuFlickable.width
height: menuFlickable.height - menuItem.menuTop
radius: popUpMenu.cornerRadius
sourceItem: menuShaderSource
fillMode: Background.Stretch
material: Material {
vertexShader: "
attribute highp vec4 position;
attribute highp vec2 normalizedPosition;
uniform highp mat4 positionMatrix;
uniform highp mat4 menuSourceMatrix;
varying highp vec2 sourceCoord;
void backgroundMain() {
gl_Position = positionMatrix * position;
sourceCoord = (menuSourceMatrix * vec4(normalizedPosition, 0, 1)).xy;
}"
fragmentShader: "
uniform lowp sampler2D sourceTexture;
varying highp vec2 sourceCoord;
void backgroundMain() {
gl_FragColor = texture2D(sourceTexture, sourceCoord);
}"
}
}
InverseMouseArea {
anchors.fill: menuShaderItem
enabled: popUpMenu.active && popUpMenu.activeBlock
stealPress: true
onPressedOutside: closed()
}
}
}
}
/*
* Copyright (с) 2022 - 2023 Open Mobile Platform LLC.
*
* 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; version 2 only.
*
* 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.2
import Sailfish.Silica 1.0
import Sailfish.Silica.private 1.0 as Private
Item {
id: root
property var popupMenu
property alias content: textContent.text
property bool isViewFull
readonly property int verticalPadding: 3 * Theme.paddingMedium
signal closeMenu
height: content.height + verticalPadding * 2
Column {
id: content
width: parent.width
spacing: Theme.paddingLarge
y: verticalPadding
InfoLabel {
id: textContent
font.pixelSize: Theme.fontSizeMedium
wrapMode: Text.Wrap
elide: Text.ElideRight
color: Theme.highlightColor
opacity: Theme.opacityHigh
}
}
}
/*
* Copyright (с) 2022 Open Mobile Platform LLC.
*
* 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; version 2 only.
*
* 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.2
import Sailfish.Silica 1.0
Rectangle {
id: root
property string text
readonly property real overlayOpacity: 0.15
signal closeMenu
height: Theme.itemSizeMedium - Theme.paddingMedium
implicitWidth: content.width
color: Qt.tint(
Theme.colorScheme === Theme.LightOnDark ? "black" : "white",
Theme.rgba(Theme.primaryColor, root.overlayOpacity))
Row {
id: content
height: root.height
width: root.width
OverlayListItem {
onClicked: {
Clipboard.text = root.text
//% "Сopied"
Notices.show(qsTrId("docviewer-me-copied_link"), Notice.Long, Notice.Center)
}
width: parent.width / 2
//% "Copy"
text: qsTrId("docviewer-me-copy")
horizontalAlignmentText: Text.AlignHCenter
}
OverlayListItem {
onClicked: closeMenu()
width: parent.width / 2
//% "Close"
text: qsTrId("docviewer-me-close")
horizontalAlignmentText: Text.AlignHCenter
}
}
}
/*
* Copyright (с) 2022 Open Mobile Platform LLC.
*
* 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; version 2 only.
*
* 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.2
import Sailfish.Silica 1.0
Item {
id: root
property var popupMenu
property alias url: linkTarget.text
readonly property int verticalPadding: 3 * Theme.paddingMedium
signal closeMenu
height: content.height + verticalPadding * 2
Column {
id: content
width: parent.width
spacing: Theme.paddingLarge
y: verticalPadding
InfoLabel {
id: linkTarget
font.pixelSize: Theme.fontSizeMedium
wrapMode: Text.Wrap
elide: Text.ElideRight
maximumLineCount: 5
color: Theme.highlightColor
opacity: Theme.opacityHigh
}
Column {
width: parent.width
OverlayListItem {
onClicked: {
closeMenu()
Qt.openUrlExternally(root.url)
}
text: (root.url.indexOf("http:") === 0
|| root.url.indexOf("https:") === 0)
//% "Open in browser"
? qsTrId("docviewer-me-link_open_browser")
//% "Open in external application"
: qsTrId("docviewer-me-link_open_external")
horizontalAlignmentText: Text.AlignHCenter
}
OverlayListItem {
onClicked: {
closeMenu()
Clipboard.text = root.url
//% "Сopied"
Notices.show(qsTrId("docviewer-me-copied_link"))
}
//% "Copy to clipboard"
text: qsTrId("docviewer-me-copy_link")
horizontalAlignmentText: Text.AlignHCenter
}
OverlayListItem {
onClicked: closeMenu()
//% "Cancel"
text: qsTrId("docviewer-me-cancel")
horizontalAlignmentText: Text.AlignHCenter
}
}
}
}
/*
* Copyright (с) 2022 - 2023 Open Mobile Platform LLC.
*
* 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; version 2 only.
*
* 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.2
import Sailfish.Silica 1.0
Item {
id: root
property int horizontalOffset
property var desktopBookmarkWriter
property bool enabledSearch: true
property bool enabledExportPdf: true
readonly property int verticalPadding: 3 * Theme.paddingSmall
readonly property int iconWidth: Theme.iconSizeMedium + Theme.paddingLarge
signal searchClicked
signal shareClicked
signal infoClicked
signal exportToPdf
signal printClicked
signal saveAsClicked
height: content.height + verticalPadding * 2
Column {
id: content
width: parent.width
spacing: Theme.paddingLarge
y: verticalPadding
Column {
width: parent.width
OverlayListItem {
onClicked: {
popupMenu.closed()
root.searchClicked()
}
enabled: root.enabledSearch
height: Theme.itemSizeSmall
iconWidth: root.iconWidth
iconSource: "image://theme/icon-m-search"
//% "Search by document"
text: qsTrId("docviewer-la-search_by_doc")
}
OverlayListItem {
onClicked: {
popupMenu.closed()
root.shareClicked()
}
height: Theme.itemSizeSmall
iconWidth: root.iconWidth
iconSource: "image://theme/icon-m-share"
//% "Share"
text: qsTrId("docviewer-la-share")
}
OverlayListItem {
onClicked: {
popupMenu.closed()
root.exportToPdf()
}
enabled: enabledExportPdf
height: Theme.itemSizeSmall
iconWidth: root.iconWidth
iconSource: "image://theme/icon-m-file-download-as-pdf"
//% "Export to PDF"
text: qsTrId("docviewer-la-export_to_pdf")
}
OverlayListItem {
onClicked: {
popupMenu.closed()
root.printClicked()
}
enabled: enabledExportPdf
height: Theme.itemSizeSmall
iconWidth: root.iconWidth
iconSource: "image://theme/icon-m-print"
//% "Print"
text: qsTrId("docviewer-la-print")
}
OverlayListItem {
onClicked: {
popupMenu.closed()
root.saveAsClicked()
}
enabled: enabledExportPdf
height: Theme.itemSizeSmall
iconWidth: root.iconWidth
iconSource: "image://theme/icon-m-save"
//% "Save as"
text: qsTrId("docviewer-la-save_as")
}
OverlayListItem {
onClicked: {
popupMenu.closed()
root.infoClicked()
}
height: Theme.itemSizeSmall
iconWidth: root.iconWidth
iconSource: "image://theme/icon-m-about"
//% "Document Information"
text: qsTrId("docviewer-la-doc_info")
}
}
}
}
/*
* Copyright (C) 2021 - 2023 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.0
import Sailfish.Silica 1.0
import Sailfish.Silica.private 1.0
Rectangle {
property bool processing
property alias text: label.text
property bool _show
onProcessingChanged: {
if (processing) {
_show = true
timer.stop()
} else {
timer.start()
}
}
color: Theme.darkPrimaryColor
width: parent.width
height: _show ? label.height + Theme.paddingMedium : 0
opacity: _show ? 1.0 : 0.0
Behavior on height { NumberAnimation { duration: 300 } }
Behavior on opacity { FadeAnimation { } }
BusyIndicator {
id: indicator
anchors {
left: parent.left
leftMargin: Theme.paddingLarge * 2
verticalCenter: parent.verticalCenter
}
running: _show
size: BusyIndicatorSize.Small
color: Theme.lightPrimaryColor
}
Label {
id: label
anchors {
left: indicator.right
leftMargin: Theme.paddingMedium
verticalCenter: parent.verticalCenter
}
color: Theme.lightPrimaryColor
font.pixelSize: Theme.fontSizeSmall
//% "Loading"
text: qsTrId("docviewer-la-processing")
}
Timer {
id: timer
interval: 1500
onTriggered: _show = false
}
}
/*
* Copyright (с) 2022 Open Mobile Platform LLC.
*
* 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; version 2 only.
*
* 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.0
import Sailfish.Silica 1.0
ProgressBar {
id: progressBar
property var document
width: parent.width
minimumValue: 0
maximumValue: 100
indeterminate: !(document.loadingProgress > 0 && document.loadingProgress < 100)
value: document.loadingProgress
anchors.verticalCenter: parent.verticalCenter
Text {
color: progressBar.highlighted ? palette.secondaryHighlightColor
: palette.secondaryColor
textFormat: Text.PlainText
text: progressBar.indeterminate ? (document.loadingProgress === 0 ? //% "Preparing to load"
qsTrId("docviewer-la-preparing")
: //% "Opening document"
qsTrId("docviewer-la-opening"))
: //% "Loading document"
qsTrId("docviewer-la-loading")
font { pixelSize: Theme.fontSizeSmall; family: Theme.fontFamily }
anchors { verticalCenter: parent.bottom; horizontalCenter: parent.horizontalCenter }
}
}
/*
* Copyright (c) 2023 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.6
import Sailfish.Silica 1.0
import Nemo.FileManager 1.0
import "utils.js" as Utils
Dialog {
id: root
property string path
property string baseName
property string sourceMimeType
property alias fileExtension: comboBox.value
canAccept: !fileName.errorHighlight
DialogHeader {
id: dialogHeader
//% "Save"
acceptText: qsTrId("docviewer-he-save")
//% "Cancel"
cancelText: qsTrId("docviewer-he-cancel")
}
SilicaFlickable {
anchors {
top: dialogHeader.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
}
contentHeight: content.height
Column {
id: content
width: parent.width
spacing: Theme.paddingLarge
InfoLabel {
horizontalAlignment: Text.AlignLeft
text: qsTrId("docviewer-ti-save_as")
}
TextField {
id: fileName
property bool fileExist
readonly property var regExp: new RegExp("[\/*\?<>\|]+")
function checkFileName() {
fileExist = FileEngine.exists("%1/%2%3".arg(path).arg(text).arg(comboBox.value))
errorHighlight = (regExp.test(text) || fileExist)
}
width: parent.width
font.pixelSize: Theme.fontSizeMedium
label: errorHighlight
? (fileExist
//% "The same name already exists"
? qsTrId("docviewer-la-name_already_exist")
//% "Invalid file name"
: qsTrId("docviewer-la-invalid_filename"))
//% "Title"
: qsTrId("docviewer-la-title_save_as")
text: baseName
placeholderText: qsTrId("docviewer-la-save_as")
onFocusChanged: if (focus) selectAll()
EnterKey.iconSource: "image://theme/icon-m-enter-accept"
EnterKey.onClicked: accept()
onTextChanged: {
checkFileName()
root.baseName = text
}
Component.onCompleted: {
text = baseName
checkFileName()
}
}
ComboBox {
id: comboBox
property var types: {
switch(Utils.docviewerType(sourceMimeType)) {
case "calc":
return [".ods", ".xls", ".xlsx"]
case "impress":
return [".odp", ".ppt", ".pptx"]
case "writer":
return [".odt", ".doc", ".docx"]
}
}
//% "Format file"
label: qsTrId("docviewer-la-format_file")
menu: ContextMenu {
Repeater {
model: comboBox.types
MenuItem {
text: modelData
}
}
}
onValueChanged: fileName.checkFileName()
}
}
}
}
/*
* Copyright (C) 2021 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.4
import Sailfish.Silica 1.0
import Sailfish.Silica.private 1.0
Rectangle {
property real zoom: 1.0
function show() {
opacity = 1.0
timer.start()
}
opacity: 0.0
visible: opacity
color: Qt.tint(Theme.darkPrimaryColor, Theme.rgba(Theme.lightPrimaryColor, Theme.opacityFaint))
radius: Theme.paddingSmall
height: label.height + Theme.paddingLarge
width: label.width + Theme.paddingLarge
onZoomChanged: show()
Behavior on opacity { FadeAnimation { } }
Label {
id: label
anchors.centerIn: parent
text: ("%1%").arg(Math.round(zoom * 100))
font.pixelSize: Theme.fontSizeSmall
font.family: "Courier"
color: Theme.lightPrimaryColor
}
Timer {
id: timer
interval: 2500
onTriggered: opacity = 0.0
}
}
/*
* Copyright (C) 2023 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.0
import Sailfish.Silica 1.0
Rectangle {
id: cursor
property bool toggled
readonly property alias active: timer.running
property Item editor: parent
property QtObject preedit
property real scale: 1.0
property bool _blinkEnabled: true
onActiveChanged: if (active) toggled = true
function reset() {
if (active) {
timer.restart()
toggled = true
}
}
visible: active && (toggled || !_blinkEnabled)
color: Theme.highlightColor
width: Math.floor(Theme.dp(2) * Math.max(1.0, scale * 0.5))
Timer {
id: timer
repeat: true
interval: 800 // TODO: use Qt.styleHints.cursorFlashTime ?
running: Qt.application.active
onTriggered: toggled = !toggled
}
}
/*
* Copyright (C) 2023 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.1
import Sailfish.Silica 1.0
import ".."
Item {
id: handle
property real fixedX
property real fixedY
property real scale
property real minimumX
property real maximumX
property QtObject contentItem
property bool moving
readonly property bool dragActive: mouseArea.drag.active
property string markerTag
// Mask area for touch handling, should cover whole mousearea
property rect mask: Qt.rect(0, 0, 0, 0)
property bool _startMarker: markerTag === "start"
width: Theme.itemSizeExtraSmall
height: width
onFixedXChanged: {
if (!moving)
timer.restart()
_startMarker ? handle.x = fixedX - width : handle.x = fixedX
}
onFixedYChanged: {
if (!moving)
timer.restart()
handle.y = fixedY
}
onDragActiveChanged: {
if (dragActive) {
moving = true
} else {
moving = false
timer.restart()
}
}
onVisibleChanged: {
if (visible) {
showAnimation.restart()
} else {
mask = Qt.rect(0, 0, 0, 0)
}
}
Image {
x: _startMarker ? handle.width - width : 0
width: Theme.iconSizeSmallPlus
height: width
transformOrigin: _startMarker ? Item.TopRight : Item.TopLeft
source: "image://theme/icon-m-textselection-" + markerTag
}
MouseArea {
id: mouseArea
property real previousX: -1
property real previousY: -1
anchors {
fill: parent
leftMargin: -Theme.paddingLarge
rightMargin: -Theme.paddingLarge
bottomMargin: -Theme.paddingLarge
}
drag {
target: parent
axis: Drag.XandYAxis
minimumX: handle.minimumX - width + Theme.iconSizeSmallPlus
minimumY: 0
maximumX: handle.maximumX
maximumY: contentItem.contentHeight
}
preventStealing: true
onPositionChanged: {
var targetX = mouse.x
var targetY = mouse.y
if (!handle.moving || Math.abs(targetX - previousX) < 0.99 && Math.abs(targetY - previousY) < 0.99) {
return
}
if (_startMarker)
contentItem.startSelectText(Qt.point(handle.x + handle.width, handle.y))
else
contentItem.endSelectText(Qt.point(handle.x, handle.y))
previousX = targetX
previousY = targetY
}
}
NumberAnimation {
id: showAnimation
target: handle
property: "opacity"
from: 0
to: 1.0
}
ParallelAnimation {
id: targetPositionAnimation
NumberAnimation {
target: handle
property: "x"
to: _startMarker ? fixedX - width : fixedX
duration: 100
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: handle
property: "y"
to: handle.fixedY
duration: 100
easing.type: Easing.InOutQuad
}
}
Timer {
id: timer
onTriggered: targetPositionAnimation.start()
interval: 300
}
}
/*
* Copyright (C) 2023 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.6
import Sailfish.Silica 1.0
Row {
id: root
property bool selectAllEnabled
property bool portrait: true
property real _buttonWidth: width / (repeater.count + 1)
readonly property var defaultButtons: {
var buttons = []
buttons.push({
"icon": "image://theme/icon-splus-share",
//% "Share"
"label": qsTrId("docviewer-la-share"),
"action": function () {
clear()
share()
}
})
buttons.push({
"icon": "image://theme/icon-splus-clipboard",
//% "Copy"
"label": qsTrId("docviewer-la-copy"),
"action": function () {
clear()
copy()
}
})
buttons.push({
"icon": "image://theme/icon-m-input-remove",
//% "Clear"
"label": qsTrId("docviewer-la-clear"),
"action": clear
})
return buttons
}
property var buttons: defaultButtons
property int fontSize: {
var size = Theme.fontSizeTiny
if (!portrait && size > Theme.fontSizeTinyBase) {
var string = ""
for (var i = 0; i < buttons.length; ++i) {
string = string + (buttons[i].label || "")
}
var requiredWidth = metrics.advanceWidth(string) + (buttons.length * (Theme.itemSizeSmall + (2 * horizontalSpacing)))
if (requiredWidth >= width - leftPadding - rightPadding) {
size = Theme.fontSizeTinyBase
}
}
return size
}
property real horizontalSpacing
signal share
signal copy
signal clear
height: Theme.itemSizeSmall + Theme.paddingLarge
FontMetrics {
id: metrics
font.pixelSize: Theme.fontSizeTiny
}
Repeater {
id: repeater
model: root.buttons
delegate: root.portrait ? portraitButtonDelegate : landscapeButtonDelegate
}
Component {
id: portraitButtonDelegate
IconButton {
id: portraitButton
width: (root.width - root.leftPadding - root.rightPadding) / Math.max(1, repeater.count)
height: root.height
icon.source: modelData.icon
icon.sourceSize: Qt.size(Theme.iconSizeSmallPlus, Theme.iconSizeSmallPlus)
icon.anchors.verticalCenterOffset: -(portraitLabel.baselineOffset - Theme.paddingSmall)
onClicked: root.buttons[index].action()
Label {
id: portraitLabel
x: (portraitButton.width - width) / 2
y: portraitButton.icon.y + portraitButton.icon.height - Theme.paddingSmall
text: modelData.label
font.pixelSize: root.fontSize
}
}
}
Component {
id: landscapeButtonDelegate
IconButton {
id: landscapeButton
width: (root.width - root.leftPadding - root.rightPadding) / Math.max(1, repeater.count)
height: root.height
icon.source: modelData.icon
icon.anchors.horizontalCenterOffset: -(Math.min(landscapeLabel.width, landscapeLabel.implicitWidth) + root.horizontalSpacing) / 2
onClicked: root.buttons[index].action()
Label {
id: landscapeLabel
x: landscapeButton.icon.x + landscapeButton.icon.width + root.horizontalSpacing
y: (landscapeButton.height - height) / 2
width: landscapeButton.width - landscapeButton.icon.width - (2 * root.horizontalSpacing)
text: modelData.label
font.pixelSize: root.fontSize
truncationMode: TruncationMode.Fade
}
}
}
}
module ru.omp.docviewer
plugin docviewerplugin
DocumentView 1.0 DocumentView.qml
DocumentPage 1.0 DocumentPage.qml
/*
* Copyright (c) 2022 - 2023 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.
*/
#include <QTimer>
#include <QSGTexture>
#include <QSGSimpleTextureNode>
#include <QPropertyAnimation>
#include <QQuickWindow>
#include <QThreadPool>
#include <QtMath>
#include <QPainter>
#include "basepage.h"
#include "bitmaploadworker.h"
#include "backgroundpage.h"
BackgroundPage::BackgroundPage(QQuickItem *parent) : QQuickItem(parent),
m_needUpdateImage(false),
m_needClearImage(false),
m_renderInProcess(false),
m_renderable(false),
m_forceRender(false),
m_imageScale(1.0),
m_pageScale(1.0),
m_debugCancel(false),
m_debugError(false)
{
setFlag(QQuickItem::ItemHasContents, true);
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &BackgroundPage::_loadBitmap);
m_timer->setInterval(150);
m_timer->setSingleShot(true);
m_timerClear = new QTimer(this);
connect(m_timerClear, &QTimer::timeout, this, &BackgroundPage::_clearImage);
m_timerClear->setInterval(5000);
m_timerClear->setSingleShot(true);
connect(this, &BackgroundPage::renderableChanged, m_timer, static_cast<void(QTimer::*)(void)>(&QTimer::start));
connect(this, &BackgroundPage::bitmapError, m_timer, static_cast<void(QTimer::*)(void)>(&QTimer::start));
connect(this, &BackgroundPage::partReady, this, &BackgroundPage::update);
m_animation = new QPropertyAnimation(this, "opacity");
m_animation->setDuration(180);
m_animation->setStartValue(0.42);
m_animation->setEndValue(1.0);
connect(m_animation, &QPropertyAnimation::finished, this, &BackgroundPage::animationEnded);
}
BackgroundPage::~BackgroundPage()
{
m_timer->stop();
m_timerClear->stop();
}
QSGNode *BackgroundPage::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
if (height() <= 0 || width() <= 0)
return nullptr;
auto node = static_cast<QSGSimpleTextureNode *>(oldNode);
if (node == nullptr) {
node = new QSGSimpleTextureNode();
node->setOwnsTexture(true);
}
if (node == nullptr)
qFatal("Error: create node error.");
if (m_needClearImage && node->texture()) {
node->texture()->deleteLater();
auto paper = QImage(1, 1, QImage::Format_RGBA8888);
paper.fill(Qt::white);
node->setTexture(window()->createTextureFromImage(paper, QQuickWindow::TextureHasAlphaChannel));
m_needClearImage = false;
}
if (node->texture() == nullptr) {
auto paper = QImage(1, 1, QImage::Format_RGBA8888);
paper.fill(Qt::white);
node->setTexture(window()->createTextureFromImage(paper, QQuickWindow::TextureHasAlphaChannel));
if (!m_pagePart.isNull())
m_needUpdateImage = true;
}
if (m_renderable && m_needUpdateImage) {
if (m_pagePart.isNull()) {
emit bitmapError();
} else {
#ifdef DEBUG_MODE
_debugDraw(&m_pagePart);
#endif
node->texture()->deleteLater();
node->setTexture(window()->createTextureFromImage(m_pagePart, QQuickWindow::TextureCanUseAtlas));
}
m_needUpdateImage = false;
}
if (m_renderable && !m_renderInProcess && m_pagePart.isNull())
emit bitmapError();
node->setRect(boundingRect());
return node;
}
void BackgroundPage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
QQuickItem::geometryChanged(newGeometry, oldGeometry);
}
void BackgroundPage::setPageSource(QSharedPointer<BasePage> pageSource)
{
if (!pageSource)
return;
m_pageSource = pageSource;
m_timer->start();
}
bool BackgroundPage::renderable() const
{
return m_renderable;
}
bool BackgroundPage::isBitmap() const
{
return !m_pagePart.isNull();
}
void BackgroundPage::setImageScale(qreal imageScale)
{
if (qFuzzyCompare(imageScale, m_imageScale))
return;
m_imageScale = imageScale;
}
void BackgroundPage::setPageScale(qreal pageScale)
{
if (qFuzzyCompare(pageScale, m_pageScale))
return;
m_pageScale = pageScale;
}
void BackgroundPage::render(bool force)
{
m_forceRender = force;
m_timer->start();
}
void BackgroundPage::setRenderable(bool renderable)
{
if (m_renderable == renderable)
return;
m_renderable = renderable;
emit renderableChanged(m_renderable);
if (!m_renderable)
QMetaObject::invokeMethod(m_timerClear, "start");
else
QMetaObject::invokeMethod(m_timerClear, "stop");
}
void BackgroundPage::_loadBitmap()
{
if (!m_pageSource)
return;
if (!m_renderable || m_renderInProcess) {
if (m_loader) {
m_loader->cancel();
m_debugCancel = true;
_updateDebugStatus();
}
return;
}
if (width() <= 0 || height() <= 0)
return;
auto pageSize = m_pageSource->originalSize();
if (!pageSize.isFinished() || pageSize.isCanceled()) {
m_timer->start();
return;
}
auto pageScaleX = width() / pageSize.result().width();
auto pageScaleY = height() / pageSize.result().height();
if (!m_forceRender && qFloor(width()) == m_pagePart.width() && qFloor(height()) == m_pagePart.height())
return;
m_loader = new BitmapLoaderWorker(m_pageSource, pageScaleX, pageScaleY, 0, m_imageScale, QPointF(x(), y()), 1 / m_pageScale * 0.3);
connect(this, &BackgroundPage::destroyed, m_loader.data(), &BitmapLoaderWorker::cancel);
connect(this, &BackgroundPage::stopRender, m_loader.data(), &BitmapLoaderWorker::cancel, Qt::DirectConnection);
connect(m_loader, &BitmapLoaderWorker::done, this, [this](QImage result) {
if (m_loader != sender())
return;
m_renderInProcess = false;
m_debugCancel = false;
m_debugError = false;
if (result.isNull()) {
emit bitmapError();
m_debugError = true;
_updateDebugStatus();
} else {
auto needAnimation = m_pagePart.isNull();
m_pagePart = result;
m_needUpdateImage = true;
emit partReady();
if (needAnimation) {
m_animation->setStartValue(m_pagePart.isNull() ? 0.42 : 0.95);
m_animation->start();
}
update();
}
});
m_pageSource->threadPool()->start(m_loader);
m_renderInProcess = true;
m_forceRender = false;
_updateDebugStatus();
}
void BackgroundPage::_clearImage()
{
if (!m_renderable) {
m_pagePart = QImage();
m_needClearImage = true;
update();
}
}
void BackgroundPage::_debugDraw(QImage *image)
{
if (m_debugError)
_debugDraw(image, Qt::red);
else if (m_debugCancel)
_debugDraw(image, Qt::black);
else if (m_renderInProcess)
_debugDraw(image, Qt::darkBlue);
else
_debugDraw(image, Qt::darkGreen);
}
void BackgroundPage::_debugDraw(QImage *image, QColor color)
{
QPainter p(image);
p.setPen(QPen(color));
p.setFont(QFont(QStringLiteral("Times"), 12, QFont::Bold));
p.drawText(image->rect(), Qt::AlignLeft | Qt::AlignTop, m_debugText);
p.drawText(image->rect(), Qt::AlignHCenter | Qt::AlignTop, QString("page: %1").arg(m_pageSource->pageIndex()));
p.drawRect(image->rect());
QString stringRect("%1,%2 %3x%4");
auto rectTile = QRect(qFloor(x()), qFloor(y()), qRound(width()), qRound(height()));
stringRect = stringRect.arg(rectTile.x()).arg(rectTile.y()).arg(rectTile.width()).arg(rectTile.height());
p.setFont(QFont(QStringLiteral("Times"), 12, QFont::Bold));
p.drawText(image->rect(), Qt::AlignCenter, stringRect);
}
void BackgroundPage::_updateDebugStatus()
{
#ifdef DEBUG_MODE
m_pagePart = QImage(qFloor(width()) + 5, qFloor(height()) + 5, QImage::Format_RGBA8888);
m_pagePart.fill(Qt::white);
m_needUpdateImage = true;
update();
#endif
}
void BackgroundPage::setDebugText(const QString &newDebugText)
{
m_debugText = newDebugText;
}
/*
* Copyright (c) 2022 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.
*/
#pragma once
#include <QQuickItem>
#include <QImage>
#include <QPointer>
class BasePage;
class QTimer;
class QSGTexture;
class QPropertyAnimation;
class BitmapLoaderWorker;
class BackgroundPage : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool renderable READ renderable WRITE setRenderable NOTIFY renderableChanged)
public:
explicit BackgroundPage(QQuickItem *parent = nullptr);
~BackgroundPage() override;
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void setPageSource(QSharedPointer<BasePage> pageSource);
bool renderable() const;
bool isBitmap() const;
void setDebugText(const QString &newDebugText);
public slots:
void setImageScale(qreal imageScale);
void setPageScale(qreal pageScale);
void render(bool force = false);
void setRenderable(bool renderable);
signals:
void bitmapError();
void partReady();
void animationEnded();
void renderableChanged(bool renderable);
void stopRender();
private slots:
void _loadBitmap();
void _clearImage();
void _debugDraw(QImage *image);
void _debugDraw(QImage *image, QColor color);
void _updateDebugStatus();
private:
QImage m_pagePart;
QSharedPointer<BasePage> m_pageSource;
bool m_needUpdateImage;
bool m_needClearImage;
bool m_renderInProcess;
bool m_renderable;
bool m_forceRender;
QTimer *m_timer;
QTimer *m_timerClear;
qreal m_imageScale;
qreal m_pageScale;
QPropertyAnimation *m_animation;
QString m_debugText;
bool m_debugCancel;
bool m_debugError;
QPointer<BitmapLoaderWorker> m_loader;
};
/*
* Copyright (c) 2022 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.
*/
#include <QQuickWindow>
#include <QSGSimpleTextureNode>
#include "basedocitem.h"
BaseDocItem::BaseDocItem(const QColor &color, const QColor &highlightcolor, QQuickItem *parent, BaseAnnotation *source)
: QQuickItem(parent)
, m_needUpdateImage(false)
, m_source(source)
, m_highlighted(false)
, m_color(color)
, m_highlightcolor(highlightcolor)
{
setFlag(QQuickItem::ItemHasContents, true);
setAcceptedMouseButtons(Qt::AllButtons);
connect(this, &BaseDocItem::xChanged, this, &BaseDocItem::update);
connect(this, &BaseDocItem::yChanged, this, &BaseDocItem::update);
connect(this, &BaseDocItem::widthChanged, this, &BaseDocItem::update);
connect(this, &BaseDocItem::heightChanged, this, &BaseDocItem::update);
connect(parent, &QQuickItem::yChanged, this, &BaseDocItem::clearHighlight);
connect(parent, &QQuickItem::xChanged, this, &BaseDocItem::clearHighlight);
m_paper = QImage(1, 1, QImage::Format_RGBA8888);
setColor(color);
}
BaseAnnotation *BaseDocItem::source() const
{
return m_source;
}
QSGNode *BaseDocItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
if (height() <= 0 || width() <= 0)
return nullptr;
auto node = static_cast<QSGSimpleTextureNode *>(oldNode);
if (node == nullptr) {
node = new QSGSimpleTextureNode();
node->setOwnsTexture(true);
}
if (m_needUpdateImage || node->texture() == nullptr) {
if (node->texture() != nullptr)
node->texture()->deleteLater();
node->setTexture(window()->createTextureFromImage(m_paper));
m_needUpdateImage = false;
}
node->setRect(boundingRect());
return node;
}
bool BaseDocItem::event(QEvent *event)
{
if (opacity() < 1.0f)
return QQuickItem::event(event);
switch (event->type()) {
case QEvent::MouseButtonPress:
setHighlight(true);
return true;
case QEvent::MouseButtonRelease:
if (source() != nullptr)
emit triggered(source());
setHighlight(false);
break;
case QEvent::MouseMove:
setHighlight(false);
break;
default:
break;
}
return QQuickItem::event(event);
}
void BaseDocItem::clearHighlight()
{
setHighlight(false);
}
void BaseDocItem::setHighlight(bool highlight)
{
if (m_highlighted != highlight) {
m_highlighted = highlight;
m_paper.fill(m_highlighted ? m_highlightcolor : m_color);
m_needUpdateImage = true;
update();
}
}
void BaseDocItem::setColor(const QColor &newColor)
{
m_color = newColor;
if (!m_highlighted) {
m_paper.fill(m_color);
m_needUpdateImage = true;
update();
}
}
void BaseDocItem::setHighlightcolor(const QColor &newHighlightcolor)
{
m_highlightcolor = newHighlightcolor;
if (!m_highlighted) {
m_paper.fill(m_highlightcolor);
m_needUpdateImage = true;
update();
}
}
bool BaseDocItem::highlighted() const
{
return m_highlighted;
}
const QColor &BaseDocItem::highlightcolor() const
{
return m_highlightcolor;
}
const QColor &BaseDocItem::color() const
{
return m_color;
}
/*
* Copyright (c) 2022 - 2023 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.
*/
#pragma once
#include <QQuickItem>
#include <QImage>
struct BaseAnnotation;
class BaseDocItem: public QQuickItem
{
Q_OBJECT
public:
BaseDocItem(const QColor &color, const QColor &highlightcolor, QQuickItem *parent = nullptr, BaseAnnotation *source = nullptr);
BaseAnnotation *source() const;
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;
const QColor &color() const;
const QColor &highlightcolor() const;
bool highlighted() const;
public slots:
bool event(QEvent *event) override;
void clearHighlight();
void setHighlight(bool highlight);
virtual void setColor(const QColor &newColor);
void setHighlightcolor(const QColor &newHighlightcolor);
signals:
void triggered(BaseAnnotation *);
protected:
bool m_needUpdateImage;
QImage m_paper;
private:
BaseAnnotation *m_source;
bool m_highlighted;
QColor m_color;
QColor m_highlightcolor;
};
/*
* Copyright (c) 2022 Open Mobile Platform LLC
*
* 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; version 2 only.
*
* 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.
*/
#include <QMutexLocker>
#include <QThread>
#include <QTimer>
#include "debug.h"
#include "basepage.h"
#include "bitmaploadworker.h"
QMutex BitmapLoaderWorker::m_mutex;
BitmapLoaderWorker::BitmapLoaderWorker(QSharedPointer<BasePage> p, qreal scaleX, qreal scaleY, int flags, qreal zoom , QPointF bias, qreal pageScale):
m_page(p),
m_scaleX(scaleX),
m_scaleY(scaleY),
m_flags(flags),
m_zoom(zoom),
m_bias(bias),
m_pageScale(pageScale),
m_isCanceled(false),
m_loaded(false)
{
}
BitmapLoaderWorker::~BitmapLoaderWorker()
{
cancel();
qCDebug(lcDocviewerRender) << "Destroy loader page index:" << m_page->pageIndex();
#ifdef DEBUG_MODE
m_page->stopBitmapPart(m_scaleX, m_scaleY, m_zoom, m_bias);
#endif
}
void BitmapLoaderWorker::run() {
if (!m_page) {
emit done({ });
return;
}
if (m_isCanceled) {
emit done({ });
return;
}
setAutoDelete(false);
connect(&m_watcher, &QFutureWatcher<QImage>::finished, this, &BitmapLoaderWorker::_getResult);
QMutexLocker lock(&m_mutex);
m_watcher.setFuture(m_page->bitmapPart(m_scaleX, m_scaleY, m_flags, m_zoom, m_bias, m_pageScale));
// If after 30 seconds no data has been received, then delete the loader
QTimer::singleShot(30 * 1000, this, &BitmapLoaderWorker::_getResult);
}
void BitmapLoaderWorker::cancel()
{
if (m_watcher.isRunning())
m_watcher.cancel();
if (m_isCanceled)
return;
if (m_page && !m_loaded)
m_page->stopBitmapPart(m_scaleX, m_scaleY, m_zoom, m_bias);
else
qCDebug(lcDocviewerRender) << "Not cancel";
m_isCanceled = true;
setAutoDelete(true);
deleteLater();
}
void BitmapLoaderWorker::_getResult()
{
if (m_watcher.isFinished() && !m_watcher.isCanceled())
emit done(m_watcher.result());
else
emit done({ });
m_loaded = true;
setAutoDelete(true);
deleteLater();
}
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать