├── .gitignore ├── README.md ├── android └── src │ └── sp │ └── SpActivity.java ├── examples ├── Arc │ ├── Arc.pro │ ├── BenchmarkItem.qml │ ├── ContactDelegate.qml │ ├── Root.qml │ ├── deployment.pri │ ├── main.cpp │ └── qml.qrc ├── ImageSp │ ├── Birthday.png │ ├── ContactDelegate.qml │ ├── ImageSp.pro │ ├── Root.qml │ ├── avatar.jpg │ ├── avatar2.jpg │ ├── deployment.pri │ ├── main.cpp │ └── qml.qrc ├── StatusBar │ ├── Android │ │ ├── AndroidManifest.xml │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── local.properties │ │ ├── res │ │ │ ├── drawable-hdpi │ │ │ │ └── icon.png │ │ │ ├── drawable-ldpi │ │ │ │ └── icon.png │ │ │ ├── drawable-mdpi │ │ │ │ └── icon.png │ │ │ └── values │ │ │ │ └── libs.xml │ │ └── src │ │ │ └── com │ │ │ └── sp │ │ │ └── statusBar │ │ │ └── StatusBarActivity.java │ ├── Images │ │ ├── Images.qrc │ │ ├── habralogo.jpg │ │ └── habralogo.png │ ├── Include │ │ └── DeviceInfo.h │ ├── QML │ │ ├── main.qml │ │ └── qml.qrc │ ├── Source │ │ ├── DeviceInfo.cpp │ │ ├── DeviceInfo.mm │ │ └── main.cpp │ ├── StatusBar.pro │ └── iOS │ │ └── Info.plist └── SwipeSelector │ ├── Card.qml │ ├── Root.qml │ ├── SwipeSelector.pro │ ├── deployment.pri │ ├── main.cpp │ └── qml.qrc ├── include ├── Arc.h ├── ArcFast.h ├── Arrow.h ├── DeviceInfo.h ├── DownloadFileHandler.h ├── ImageParallax.h ├── ImageSaverSp.h ├── ImageSp.h ├── KeyboardSp.h ├── LogSp.h ├── Net.h ├── Settings.h ├── Shadow.h ├── SpApplicationPrototype.h └── private │ ├── ImageSpLoader.h │ ├── ImageSpNode.h │ └── NetHandler.h ├── qml ├── ActionBar.qml ├── ActionBarImage.qml ├── BackButton.qml ├── ButtonPrototype.qml ├── ConstsPrototype.qml ├── Counter.qml ├── Debug.qml ├── DebugLayout.qml ├── DebugRectangle.qml ├── DebugTimer.qml ├── DrawerPrototype.qml ├── FullScreenImage.qml ├── FullScreenImageView.qml ├── KeysHandler.qml ├── MaterialButton.qml ├── MenuButton.qml ├── ShadowBottom.qml ├── ShadowRectangle.qml ├── ShadowTop.qml ├── SmileForRate.qml ├── StackView.qml ├── SwipeAnimationDelegate.qml ├── SwipeSelector.qml ├── SwipeView.qml ├── TextBig.qml ├── TextNormal.qml ├── TextSmall.qml ├── Toast.qml ├── Zoom.qml ├── qml.qrc └── qmldir ├── source ├── Arc.cpp ├── ArcFast.cpp ├── Arrow.cpp ├── DeviceInfo.cpp ├── DownloadFileHandler.cpp ├── ImageParallax.cpp ├── ImageSaverSp.cpp ├── ImageSp.cpp ├── KeyboardSp.cpp ├── KeyboardSp.mm ├── LogSp.cpp ├── Net.cpp ├── Settings.cpp ├── Shadow.cpp ├── SpApplicationPrototype.cpp ├── SpApplicationPrototype.mm └── private │ ├── ImageSpLoader.cpp │ ├── ImageSpNode.cpp │ └── NetHandler.cpp ├── sp_qt_libs.pri └── sp_qt_libs.qbs /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | *.autosave 3 | Examples/build* 4 | Examples/*/Programmer.pri 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Siberian Programmers Qt Libs 2 | 3 | `sp_qt_libs` - это C++11/Qt/QML библиотека, предназначенная для разработки мобильных приложенией под *Android* и *iOS*. 4 | В её состав входит: 5 | * Базовый класс приложения `SpApplicationPrototype`; 6 | * Набор QML-компонентов для создания и отладки интерфейса; 7 | * Простая и чистая система логирования (особенно полезно в Android и для профилирования) 8 | * Сетевой стек для обращения к интернету в условиях нестабильной мобильной сети (пересылка запроса после разрыва связи, докачка файлов и др.) 9 | * Базовая Activity для Android. 10 | * Набор примеров для демонстрации библиотечных QML-компонентов. 11 | 12 | ## Требования для сборки 13 | Библиотеку тестировали на Apple OS X и Linux с Qt 5.8, но должна работать и с более ранними версиями Qt (нужно поменять версию import'а QML-компонетов). 14 | 15 | ## Логирование и отладка 16 | В рамках библиотеки была расширена технология логирования Qt классом `sp::Log` и QML-синглетоном `Debug`. 17 | Во-первых, для каждого программиста предусматривается отдельная функция для вывода в лог. Эта техника позволяет сохранить лог чистым от отладочного вывода других программистов, работающих над проектом. В файле `Programmer.pri` должно быть определение лога программиста, к примеру: 18 | ``` 19 | DEFINES += SP_ALEUS 20 | ``` 21 | А в файле `LogSp.h` находится следующее определение 22 | ```C++ 23 | #ifdef SP_ALEUS 24 | #define LOG_ALEUS(str) qDebug() << str 25 | #else 26 | #define LOG_ALEUS(str) ; 27 | #endif 28 | ``` 29 | Рекомендую по аналогии добавить собственную функцию и флаг логирования. 30 | 31 | Во-вторых, изменился формат логов на для _Android_: 32 | ``` 33 | ####### : 9999 : Текст лога 34 | ``` 35 | , а для _Desktop_ и _iOS_: 36 | ``` 37 | 9999 : Текст лога 38 | ``` 39 | Здесь `9999` - это время в милисекундах от начала работы программы, а `#######` - это метка для вырезания лога приложения в *Android* (по-умолчанию, Qt продуцирует несколько меток, что неудобно) 40 | ```bash 41 | adb logcat | grep "#######" 42 | ``` 43 | В-третьих, для в Qt Quick иногда требуется узнать содержимое объекта, для этого можно использовать команду 44 | ``` 45 | Debug.printObject (object, "Пояснение") 46 | ``` 47 | -------------------------------------------------------------------------------- /examples/Arc/Arc.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += qml quick widgets 4 | CONFIG += c++11 5 | 6 | SOURCES += main.cpp 7 | RESOURCES += qml.qrc 8 | 9 | include(deployment.pri) 10 | include($$PWD/../../sp_qt_libs.pri) 11 | -------------------------------------------------------------------------------- /examples/Arc/BenchmarkItem.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.5 2 | import DXS 1.0 3 | 4 | Item { 5 | id: _benchmarkItem 6 | 7 | 8 | anchors.fill: parent 9 | 10 | } 11 | -------------------------------------------------------------------------------- /examples/Arc/ContactDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.5 2 | import QtQuick.Window 2.2 3 | import DXS 1.0 4 | 5 | Item { 6 | id: _contactDelegate 7 | 8 | width: listView.width 9 | height: avatar.height + margin 10 | 11 | //-------------------------------------------------------------------------- 12 | ImageDxs { 13 | id: avatar 14 | 15 | source: "qrc:/avatar.jpg" 16 | width: 10*mm 17 | height: width 18 | radius: width/2 19 | asynchronous: true 20 | anchors { 21 | left: parent.left 22 | leftMargin: margin 23 | verticalCenter: parent.verticalCenter 24 | } 25 | } 26 | 27 | //-------------------------------------------------------------------------- 28 | Text { 29 | id: nameItem 30 | 31 | text: index + " Маргарита Ивановна Сушина" 32 | anchors { 33 | left: avatar.right 34 | right: parent.right 35 | margins: margin 36 | verticalCenter: parent.verticalCenter 37 | } 38 | elide: Text.ElideRight 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/Arc/Root.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.5 2 | import SP 1.0 3 | 4 | Item { 5 | readonly property double margin: 2*mm 6 | property bool fast: false 7 | 8 | width: Window.width 9 | height: Window.height 10 | 11 | //-------------------------------------------------------------------------- 12 | Loader { 13 | id: arcLoader 14 | 15 | sourceComponent: fast 16 | ? arcFastComponent 17 | : arcComponent 18 | anchors { 19 | fill: parent 20 | margins: margin 21 | } 22 | } 23 | 24 | //-------------------------------------------------------------------------- 25 | MouseArea { 26 | anchors.fill: parent 27 | onClicked: { 28 | animation.stop(); 29 | fast = !fast; 30 | } 31 | 32 | Column { 33 | anchors.centerIn: parent 34 | spacing: margin 35 | 36 | Text { 37 | id: text 38 | text: fast 39 | ? "ArcFast" 40 | : "Arc" 41 | font.pointSize: 14 42 | anchors.horizontalCenter: parent.horizontalCenter 43 | } 44 | Text { 45 | text: "Нажми, чтобы\nсменить компонент." 46 | color: "darkgray" 47 | font.pointSize: 12 48 | anchors.horizontalCenter: parent.horizontalCenter 49 | horizontalAlignment: Text.AlignHCenter 50 | } 51 | } 52 | } 53 | 54 | //-------------------------------------------------------------------------- 55 | // Дуга с отрисовкой в QPainter 56 | //-------------------------------------------------------------------------- 57 | Component { 58 | id: arcComponent 59 | 60 | Arc { 61 | spanAngle: 0 62 | penWidth: 0.25*mm 63 | } 64 | } 65 | 66 | //-------------------------------------------------------------------------- 67 | // Дуга с отрисовкой в SceneGraph 68 | //-------------------------------------------------------------------------- 69 | Component { 70 | id: arcFastComponent 71 | 72 | ArcFast { 73 | spanAngle: 0 74 | penWidth: 0.25*mm 75 | } 76 | } 77 | 78 | //-------------------------------------------------------------------------- 79 | SequentialAnimation { 80 | id: animation 81 | 82 | running: arcLoader.status === Loader.Ready 83 | loops: Animation.Infinite 84 | 85 | NumberAnimation { 86 | target: arcLoader.item 87 | property: "spanAngle" 88 | from: 0 89 | to: 360 90 | duration: 1000 91 | } 92 | ParallelAnimation { 93 | NumberAnimation { 94 | target: arcLoader.item 95 | property: "spanAngle" 96 | from: 360 97 | to: 0 98 | duration: 1000 99 | } 100 | NumberAnimation { 101 | target: arcLoader.item 102 | property: "startAngle" 103 | from: 0 104 | to: 360 105 | duration: 1000 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/Arc/deployment.pri: -------------------------------------------------------------------------------- 1 | unix:!android { 2 | isEmpty(target.path) { 3 | qnx { 4 | target.path = /tmp/$${TARGET}/bin 5 | } else { 6 | target.path = /opt/$${TARGET}/bin 7 | } 8 | export(target.path) 9 | } 10 | INSTALLS += target 11 | } 12 | 13 | export(INSTALLS) 14 | -------------------------------------------------------------------------------- /examples/Arc/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | sp::SpApplicationPrototype app(argc, argv, "Arc Example"); 6 | 7 | return app.exec(); 8 | } 9 | -------------------------------------------------------------------------------- /examples/Arc/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Root.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/ImageSp/Birthday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiberianProgrammers/sp_qt_libs/e8b2a8edc83d988bd70df64a858731602c9e4b47/examples/ImageSp/Birthday.png -------------------------------------------------------------------------------- /examples/ImageSp/ContactDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.5 2 | import SP 1.0 3 | 4 | Item { 5 | id: _contactDelegate 6 | 7 | property bool expanded: false 8 | 9 | width: listView.width 10 | height: 16*mm 11 | 12 | //-------------------------------------------------------------------------- 13 | ImageSp { 14 | id: avatar 15 | 16 | property real r: width/2 17 | source: "qrc:/avatar.jpg" 18 | radius: r 19 | x: margin 20 | y: (parent.height-avatar.height)/2 21 | height: parent.height - margin 22 | width: height 23 | 24 | MouseArea { 25 | anchors.fill: parent 26 | onClicked: { 27 | if (!expanded) { 28 | viaElement.x = _contactDelegate.mapToItem(root,0,0).x; 29 | viaElement.y = _contactDelegate.mapToItem(root,0,0).y; 30 | } 31 | expanded = !expanded; 32 | } 33 | } 34 | } 35 | 36 | //-------------------------------------------------------------------------- 37 | Text { 38 | id: nameItem 39 | 40 | text: index + " Маргарита Ивановна Сушина" 41 | anchors { 42 | left: parent.left 43 | right: parent.right 44 | margins: margin 45 | leftMargin: parent.height + margin 46 | verticalCenter: parent.verticalCenter 47 | } 48 | elide: Text.ElideRight 49 | } 50 | 51 | //-------------------------------------------------------------------------- 52 | Item { 53 | id: statesItem 54 | 55 | state: expanded 56 | ? "expanded" 57 | : "normal" 58 | states: [ 59 | State { 60 | name: "normal" 61 | 62 | PropertyChanges { 63 | target: avatar 64 | r: avatar.height/2 65 | } 66 | ParentChange { 67 | target: avatar 68 | parent: _contactDelegate 69 | } 70 | PropertyChanges { 71 | target: containerBackground 72 | explicit: true 73 | opacity: 0.0 74 | } 75 | } 76 | ,State { 77 | name: "expanded" 78 | 79 | PropertyChanges { 80 | target: avatar 81 | r: 0 82 | } 83 | ParentChange { 84 | target: avatar 85 | parent: container 86 | x: margin 87 | y: (parent.height-avatar.height)/2 88 | width: parent.width - 2*margin 89 | height: width 90 | } 91 | PropertyChanges { 92 | target: containerBackground 93 | explicit: true 94 | opacity: 1.0 95 | } 96 | } 97 | ] // states: [ 98 | 99 | transitions: [ 100 | Transition { 101 | SequentialAnimation { 102 | ParallelAnimation { 103 | NumberAnimation { 104 | properties: "r,opacity" 105 | duration: 300 106 | } 107 | ParentAnimation { 108 | via: viaElement 109 | NumberAnimation { 110 | properties: "x,y,width,height" 111 | duration: 300 112 | } 113 | } 114 | } // ParallelAnimation { 115 | } 116 | } 117 | ] 118 | } // Item { id: statesItem 119 | } 120 | -------------------------------------------------------------------------------- /examples/ImageSp/ImageSp.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += qml quick widgets 4 | CONFIG += c++11 5 | 6 | SOURCES += main.cpp 7 | RESOURCES += qml.qrc 8 | 9 | include(deployment.pri) 10 | include(Programmer.pri) 11 | include($$PWD/../../sp_qt_libs.pri) 12 | 13 | android { 14 | QT += androidextras 15 | HEADERS += $$PWD/../../../sp_project_prototype/Include/JniSetup.h 16 | } 17 | -------------------------------------------------------------------------------- /examples/ImageSp/Root.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.5 2 | import SP 1.0 3 | 4 | Item { 5 | id: root 6 | 7 | readonly property real margin: 3*mm 8 | 9 | visible: true 10 | width: Window.width 11 | height: Window.height 12 | 13 | //-------------------------------------------------------------------------- 14 | //ListView { 15 | // id: listView 16 | 17 | // model: 400 18 | // delegate: ContactDelegate {} 19 | // anchors.fill: parent 20 | // maximumFlickVelocity: 8*height 21 | //} 22 | 23 | //Rectangle { 24 | // id: containerBackground 25 | 26 | // anchors.fill: container 27 | // color: "white" 28 | // opacity: 0.0 29 | //} 30 | 31 | //Item { 32 | // id: container 33 | 34 | // anchors.fill: parent 35 | //} 36 | 37 | //Item { 38 | // id: viaElement 39 | //} 40 | 41 | //-------------------------------------------------------------------------- 42 | ImageSp { 43 | property bool avatarSwitch: true 44 | 45 | source: avatarSwitch 46 | ? "qrc:/avatar.jpg" 47 | : "qrc:/avatar2.jpg" 48 | //fillMode: ImageSp.PreserveAspectFit 49 | radius: 5*mm 50 | anchors { 51 | fill: parent 52 | margins: margin 53 | } 54 | 55 | MouseArea { 56 | anchors.fill: parent 57 | onClicked: { 58 | parent.avatarSwitch = !parent.avatarSwitch; 59 | //parent.mipmap = !parent.mipmap; 60 | } 61 | } 62 | }// ImageFast { 63 | } 64 | -------------------------------------------------------------------------------- /examples/ImageSp/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiberianProgrammers/sp_qt_libs/e8b2a8edc83d988bd70df64a858731602c9e4b47/examples/ImageSp/avatar.jpg -------------------------------------------------------------------------------- /examples/ImageSp/avatar2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiberianProgrammers/sp_qt_libs/e8b2a8edc83d988bd70df64a858731602c9e4b47/examples/ImageSp/avatar2.jpg -------------------------------------------------------------------------------- /examples/ImageSp/deployment.pri: -------------------------------------------------------------------------------- 1 | unix:!android { 2 | isEmpty(target.path) { 3 | qnx { 4 | target.path = /tmp/$${TARGET}/bin 5 | } else { 6 | target.path = /opt/$${TARGET}/bin 7 | } 8 | export(target.path) 9 | } 10 | INSTALLS += target 11 | } 12 | 13 | export(INSTALLS) 14 | -------------------------------------------------------------------------------- /examples/ImageSp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | sp::SpApplicationPrototype app(argc, argv, "ImageSp Example"); 6 | 7 | return app.exec(); 8 | } 9 | -------------------------------------------------------------------------------- /examples/ImageSp/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Root.qml 4 | avatar.jpg 5 | ContactDelegate.qml 6 | avatar2.jpg 7 | Birthday.png 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/StatusBar/Android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 71 | 72 | 73 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /examples/StatusBar/Android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:1.1.0' 8 | } 9 | } 10 | 11 | allprojects { 12 | repositories { 13 | jcenter() 14 | } 15 | } 16 | 17 | apply plugin: 'com.android.application' 18 | 19 | dependencies { 20 | compile fileTree(dir: 'libs', include: ['*.jar']) 21 | } 22 | 23 | android { 24 | /******************************************************* 25 | * The following variables: 26 | * - androidBuildToolsVersion, 27 | * - androidCompileSdkVersion 28 | * - qt5AndroidDir - holds the path to qt android files 29 | * needed to build any Qt application 30 | * on Android. 31 | * 32 | * are defined in gradle.properties file. This file is 33 | * updated by QtCreator and androiddeployqt tools. 34 | * Changing them manually might break the compilation! 35 | *******************************************************/ 36 | 37 | compileSdkVersion androidCompileSdkVersion.toInteger() 38 | 39 | buildToolsVersion androidBuildToolsVersion 40 | 41 | sourceSets { 42 | main { 43 | manifest.srcFile 'AndroidManifest.xml' 44 | java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] 45 | aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] 46 | res.srcDirs = [qt5AndroidDir + '/res', 'res'] 47 | resources.srcDirs = ['src'] 48 | renderscript.srcDirs = ['src'] 49 | assets.srcDirs = ['assets'] 50 | jniLibs.srcDirs = ['libs'] 51 | } 52 | } 53 | 54 | lintOptions { 55 | abortOnError false 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/StatusBar/Android/gradle.properties: -------------------------------------------------------------------------------- 1 | ## This file is automatically generated by QtCreator. 2 | # 3 | # This file must *NOT* be checked into Version Control Systems, 4 | # as it contains information specific to your local configuration. 5 | 6 | androidBuildToolsVersion=23.0.3 7 | androidCompileSdkVersion=23 8 | buildDir=.build 9 | qt5AndroidDir=/Users/dukkham/Qt5.8.0/5.8/android_armv7/src/android/java 10 | -------------------------------------------------------------------------------- /examples/StatusBar/Android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiberianProgrammers/sp_qt_libs/e8b2a8edc83d988bd70df64a858731602c9e4b47/examples/StatusBar/Android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/StatusBar/Android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 7 | -------------------------------------------------------------------------------- /examples/StatusBar/Android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /examples/StatusBar/Android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /examples/StatusBar/Android/local.properties: -------------------------------------------------------------------------------- 1 | ## This file is automatically generated by QtCreator. 2 | # 3 | # This file must *NOT* be checked into Version Control Systems, 4 | # as it contains information specific to your local configuration. 5 | 6 | sdk.dir=/Users/dukkham/Android/android-sdk-macosx 7 | -------------------------------------------------------------------------------- /examples/StatusBar/Android/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiberianProgrammers/sp_qt_libs/e8b2a8edc83d988bd70df64a858731602c9e4b47/examples/StatusBar/Android/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /examples/StatusBar/Android/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiberianProgrammers/sp_qt_libs/e8b2a8edc83d988bd70df64a858731602c9e4b47/examples/StatusBar/Android/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /examples/StatusBar/Android/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiberianProgrammers/sp_qt_libs/e8b2a8edc83d988bd70df64a858731602c9e4b47/examples/StatusBar/Android/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /examples/StatusBar/Android/res/values/libs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://download.qt.io/ministro/android/qt5/qt-5.7 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/StatusBar/Android/src/com/sp/statusBar/StatusBarActivity.java: -------------------------------------------------------------------------------- 1 | package com.sp.statusBar; 2 | 3 | import org.qtproject.qt5.android.bindings.QtActivity; 4 | import android.app.Activity; 5 | import android.os.Bundle; 6 | import android.os.Build; 7 | import android.view.Window; 8 | import android.view.WindowManager; 9 | import android.view.View; 10 | import android.graphics.Color; 11 | import android.content.Context; 12 | 13 | public class StatusBarActivity extends QtActivity 14 | { 15 | @Override 16 | public void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | 19 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 20 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 21 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 22 | getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 23 | getWindow().setStatusBarColor(Color.TRANSPARENT); 24 | } else { 25 | getWindow().setFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS 26 | , WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 27 | } 28 | } 29 | } 30 | 31 | //-------------------------------------------------------------------------- 32 | // @brief Возвращает высоту status bar 33 | //-------------------------------------------------------------------------- 34 | public int statusBarHeight() { 35 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 36 | return 0; 37 | } 38 | 39 | int result = 0; 40 | int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); 41 | if (resourceId > 0) { 42 | result = getResources().getDimensionPixelSize(resourceId); 43 | } 44 | 45 | return result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/StatusBar/Images/Images.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | habralogo.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/StatusBar/Images/habralogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiberianProgrammers/sp_qt_libs/e8b2a8edc83d988bd70df64a858731602c9e4b47/examples/StatusBar/Images/habralogo.jpg -------------------------------------------------------------------------------- /examples/StatusBar/Images/habralogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiberianProgrammers/sp_qt_libs/e8b2a8edc83d988bd70df64a858731602c9e4b47/examples/StatusBar/Images/habralogo.png -------------------------------------------------------------------------------- /examples/StatusBar/Include/DeviceInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class DeviceInfo : public QObject 6 | { 7 | Q_OBJECT 8 | 9 | public: 10 | DeviceInfo(QObject *parent = NULL); 11 | static DeviceInfo &instance(QObject *parent = 0); 12 | 13 | Q_INVOKABLE int statusBarHeight(); 14 | 15 | private: 16 | static DeviceInfo _instance; 17 | }; 18 | -------------------------------------------------------------------------------- /examples/StatusBar/QML/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | 3 | Item { 4 | id: window 5 | 6 | height: Window.height 7 | width : Window.width 8 | 9 | Rectangle { 10 | anchors.fill: parent 11 | color: "#78a3b6" 12 | } 13 | 14 | Image { 15 | source: "qrc:/images/habralogo.png" 16 | width: 0.8*parent.width 17 | anchors.centerIn: parent 18 | fillMode: Image.PreserveAspectFit 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/StatusBar/QML/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/StatusBar/Source/DeviceInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "DeviceInfo.h" 2 | 3 | #if defined(Q_OS_ANDROID) 4 | #include 5 | #include 6 | #include 7 | #endif 8 | 9 | DeviceInfo::DeviceInfo(QObject *parent) 10 | : QObject(parent) 11 | { 12 | 13 | } 14 | 15 | //------------------------------------------------------------------------------ 16 | DeviceInfo &DeviceInfo::instance(QObject *parent) 17 | { 18 | static DeviceInfo instance(parent); 19 | return instance; 20 | } 21 | 22 | //------------------------------------------------------------------------------ 23 | int DeviceInfo::statusBarHeight() 24 | { 25 | #if defined (Q_OS_ANDROID) 26 | QAndroidJniObject activity = QtAndroid::androidActivity(); 27 | jint height = activity.callMethod("statusBarHeight"); 28 | 29 | return (int) height; 30 | #endif 31 | return 0; 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/StatusBar/Source/DeviceInfo.mm: -------------------------------------------------------------------------------- 1 | #include "DeviceInfo.h" 2 | 3 | #import 4 | 5 | DeviceInfo::DeviceInfo(QObject *parent) 6 | : QObject(parent) 7 | { 8 | [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; 9 | } 10 | 11 | //------------------------------------------------------------------------------ 12 | DeviceInfo &DeviceInfo::instance(QObject *parent) 13 | { 14 | static DeviceInfo instance(parent); 15 | return instance; 16 | } 17 | 18 | //------------------------------------------------------------------------------ 19 | int DeviceInfo::statusBarHeight() 20 | { 21 | return [UIApplication sharedApplication].statusBarFrame.size.height; 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/StatusBar/Source/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "DeviceInfo.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | QGuiApplication app(argc, argv); 9 | 10 | QQuickView view; 11 | 12 | #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) 13 | view.setWidth(app.primaryScreen()->size().width()); 14 | view.setHeight(app.primaryScreen()->size().height()); 15 | #else 16 | view.setWidth(360); 17 | view.setHeight(280); 18 | #endif 19 | 20 | const double mmInInch = 25.4; 21 | double mm = app.screens().first()->physicalDotsPerInch()/mmInInch; 22 | 23 | view.rootContext()->setContextProperty("mm", mm); 24 | view.rootContext()->setContextProperty("DeviceInfo", &DeviceInfo::instance()); 25 | view.rootContext()->setContextProperty("Window", &view); 26 | 27 | view.setSource(QUrl(QStringLiteral("qrc:/main.qml"))); 28 | 29 | #ifdef Q_OS_IOS 30 | view.showFullScreen(); 31 | #else 32 | view.show(); 33 | #endif 34 | 35 | return app.exec(); 36 | } 37 | -------------------------------------------------------------------------------- /examples/StatusBar/StatusBar.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += qml quick 4 | CONFIG += c++11 5 | 6 | INCLUDEPATH += Include 7 | 8 | HEADERS += \ 9 | Include/DeviceInfo.h 10 | 11 | SOURCES += \ 12 | Source/main.cpp 13 | 14 | RESOURCES += \ 15 | QML/qml.qrc \ 16 | Images/Images.qrc 17 | 18 | android { 19 | QT += androidextras 20 | 21 | DISTFILES += \ 22 | Android/AndroidManifest.xml \ 23 | Android/gradle/wrapper/gradle-wrapper.jar \ 24 | Android/gradlew \ 25 | Android/res/values/libs.xml \ 26 | Android/build.gradle \ 27 | Android/gradle/wrapper/gradle-wrapper.properties \ 28 | Android/gradlew.bat \ 29 | Android/src/com/sp/statusBar/StatusBarActivity.java 30 | 31 | ANDROID_PACKAGE_SOURCE_DIR = $$PWD/Android 32 | } 33 | 34 | !ios { 35 | SOURCES += \ 36 | Source/DeviceInfo.cpp 37 | } 38 | 39 | ios { 40 | OBJECTIVE_SOURCES += \ 41 | Source/DeviceInfo.mm 42 | 43 | QMAKE_INFO_PLIST = iOS/Info.plist 44 | } 45 | 46 | # Default rules for deployment. 47 | qnx: target.path = /tmp/$${TARGET}/bin 48 | else: unix:!android: target.path = /opt/$${TARGET}/bin 49 | !isEmpty(target.path): INSTALLS += target 50 | 51 | 52 | -------------------------------------------------------------------------------- /examples/StatusBar/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIconFile 6 | 7 | CFBundlePackageType 8 | APPL 9 | CFBundleGetInfoString 10 | Created by Qt/QMake 11 | CFBundleSignature 12 | ???? 13 | CFBundleExecutable 14 | StatusBar 15 | CFBundleIdentifier 16 | test.${PRODUCT_NAME:rfc1034identifier} 17 | CFBundleDisplayName 18 | ${PRODUCT_NAME} 19 | CFBundleName 20 | ${PRODUCT_NAME} 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationPortraitUpsideDown 33 | UIInterfaceOrientationLandscapeLeft 34 | UIInterfaceOrientationLandscapeRight 35 | 36 | UIViewControllerBasedStatusBarAppearance 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/SwipeSelector/Card.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.5 2 | import "qrc:/SpQml" 3 | 4 | Rectangle { 5 | id: debugDelegate 6 | 7 | property bool onTop: true 8 | readonly property alias swipeSelector: swipeSelector 9 | 10 | x: swipeSelector.originX 11 | y: swipeSelector.originY 12 | radius: 2*mm 13 | width: parent.width-2*margin 14 | height: parent.height-20*mm 15 | color: "#4ba4db" 16 | z: onTop 17 | ? 0 18 | : -1 19 | scale: onTop 20 | ? 1.0 21 | : 0.8 + 0.2*swipeSelectorTop.factor 22 | 23 | //---------------------------------------------------------------------- 24 | SwipeSelector { 25 | id: swipeSelector 26 | 27 | mayBottomSelected: false 28 | 29 | onTopSelected: print ("top selected") 30 | onLeftSelected: print ("left selected") 31 | onRightSelected: print ("right selected") 32 | onBottomSelected: print ("bottom selected") 33 | 34 | onCardRemoved: { 35 | blueOnTop = !blueOnTop; 36 | x = swipeSelector.originX; 37 | y = swipeSelector.originY; 38 | } 39 | } // Item { id: swipeSelector 40 | 41 | //-------------------------------------------------------------------------- 42 | Text { 43 | text: "right" 44 | anchors { 45 | top: parent.top 46 | left: parent.left 47 | margins: margin 48 | } 49 | font { 50 | bold: true 51 | pointSize: 17 52 | } 53 | opacity: onTop 54 | ? -swipeSelector.factorX 55 | : 0.0 56 | } 57 | 58 | Text { 59 | text: "left" 60 | anchors { 61 | top: parent.top 62 | right: parent.right 63 | margins: margin 64 | } 65 | font { 66 | bold: true 67 | pointSize: 17 68 | } 69 | opacity: onTop 70 | ? swipeSelector.factorX 71 | : 0.0 72 | } 73 | 74 | Text { 75 | text: "top" 76 | anchors { 77 | bottom: parent.bottom 78 | margins: margin 79 | horizontalCenter: parent.horizontalCenter 80 | } 81 | font { 82 | bold: true 83 | pointSize: 17 84 | } 85 | opacity: onTop 86 | ? swipeSelector.factorY 87 | : 0.0 88 | } 89 | 90 | Text { 91 | text: "bottom" 92 | anchors { 93 | top: parent.top 94 | margins: margin 95 | horizontalCenter: parent.horizontalCenter 96 | } 97 | font { 98 | bold: true 99 | pointSize: 17 100 | } 101 | opacity: onTop 102 | ? -swipeSelector.factorY 103 | : 0 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /examples/SwipeSelector/Root.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.5 2 | import SP 1.0 3 | import "qrc:/SpQml" 4 | 5 | Item { 6 | width: Window.width 7 | height: Window.height 8 | 9 | property bool blueOnTop: true 10 | property SwipeSelector swipeSelectorTop: blueOnTop 11 | ? blue.swipeSelector 12 | : green.swipeSelector 13 | property real margin: 4*mm 14 | 15 | Card { 16 | id: blue 17 | 18 | onTop: blueOnTop 19 | } 20 | 21 | Card { 22 | id: green 23 | 24 | color: "green" 25 | onTop: !blueOnTop 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/SwipeSelector/SwipeSelector.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += qml quick 4 | CONFIG += c++11 5 | 6 | SOURCES += main.cpp 7 | RESOURCES += qml.qrc 8 | 9 | include(deployment.pri) 10 | include($$PWD/../../sp_qt_libs.pri) 11 | -------------------------------------------------------------------------------- /examples/SwipeSelector/deployment.pri: -------------------------------------------------------------------------------- 1 | unix:!android { 2 | isEmpty(target.path) { 3 | qnx { 4 | target.path = /tmp/$${TARGET}/bin 5 | } else { 6 | target.path = /opt/$${TARGET}/bin 7 | } 8 | export(target.path) 9 | } 10 | INSTALLS += target 11 | } 12 | 13 | export(INSTALLS) 14 | -------------------------------------------------------------------------------- /examples/SwipeSelector/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | sp::SpApplicationPrototype app(argc, argv, "Swipe Selector Example"); 6 | 7 | return app.exec(); 8 | } 9 | -------------------------------------------------------------------------------- /examples/SwipeSelector/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Root.qml 4 | Card.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /include/Arc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace sp { 7 | /** 8 | * @brief Класс для отрисовки дуги. Использует QPainter. Для быстрой отрисовки смотри ArcFast 9 | */ 10 | class Arc : public QQuickPaintedItem 11 | { 12 | Q_OBJECT 13 | Q_INTERFACES(QQmlParserStatus) 14 | 15 | Q_PROPERTY (double penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged) 16 | Q_PROPERTY (double arcHeight READ arcHeight WRITE setArcHeight NOTIFY arcHeightChanged) 17 | /** @brief Начальный угол */ 18 | Q_PROPERTY (double startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged) 19 | /** @brief Угловая длина дуги */ 20 | Q_PROPERTY (double spanAngle READ spanAngle WRITE setSpanAngle NOTIFY spanAngleChanged) 21 | Q_PROPERTY (QColor color READ color WRITE setColor NOTIFY colorChanged) 22 | 23 | public: 24 | Arc(QQuickItem *parent = nullptr); 25 | 26 | virtual void classBegin() override; 27 | virtual void componentComplete() override; 28 | virtual void paint(QPainter *painter) override; 29 | 30 | double penWidth() const; 31 | double arcHeight() const; 32 | double startAngle() const; 33 | double spanAngle() const; 34 | QColor color() const; 35 | 36 | void setPenWidth(double penWidth); 37 | void setArcHeight(double arcHeight); 38 | void setStartAngle(double startAngle); 39 | void setSpanAngle(double spanAngle); 40 | void setColor(const QColor &color); 41 | 42 | signals: 43 | void penWidthChanged(double penWidth); 44 | void arcHeightChanged(double arcHeight); 45 | void startAngleChanged(double startAngle); 46 | void spanAngleChanged(double spanAngle); 47 | void colorChanged(QColor color); 48 | 49 | protected: 50 | bool _complete = false; 51 | double _penWidth = 2; 52 | double _startAngle = 0; 53 | double _spanAngle = 0; 54 | QColor _color = Qt::black; 55 | double _arcHeight = 0; 56 | }; 57 | } // namespace sp { 58 | -------------------------------------------------------------------------------- /include/ArcFast.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace sp { 6 | /** 7 | * @brief Класс для отрисовки дуги через Scene Graph. Для более плавной отрисовки смотри Arc 8 | * @note Антиалиасинг осуществляется с помощью QSurfaceFormat format; format.setSample(16). 9 | * Подробнее смотри в примере в файле main.cpp. 10 | */ 11 | class ArcFast : public QQuickItem 12 | { 13 | Q_OBJECT 14 | Q_INTERFACES(QQmlParserStatus) 15 | 16 | Q_PROPERTY (double penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged) 17 | /** @brief Начальный угол */ 18 | Q_PROPERTY (double startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged) 19 | /** @brief Угловая длина дуги */ 20 | Q_PROPERTY (double spanAngle READ spanAngle WRITE setSpanAngle NOTIFY spanAngleChanged) 21 | Q_PROPERTY (QColor color READ color WRITE setColor NOTIFY colorChanged) 22 | Q_PROPERTY (int segmentCount READ segmentCount WRITE setSegmentCount NOTIFY segmentCountChanged) 23 | 24 | public: 25 | ArcFast(QQuickItem *parent = nullptr); 26 | 27 | virtual void classBegin() override; 28 | virtual void componentComplete() override; 29 | QSGNode* updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; 30 | 31 | double penWidth() const; 32 | double startAngle() const; 33 | double spanAngle() const; 34 | QColor color() const; 35 | int segmentCount() const; 36 | 37 | void setPenWidth(double penWidth); 38 | void setStartAngle(double startAngle); 39 | void setSpanAngle(double spanAngle); 40 | void setColor(const QColor &color); 41 | void setSegmentCount(int segmentCount); 42 | 43 | signals: 44 | void penWidthChanged(double penWidth); 45 | void startAngleChanged(double startAngle); 46 | void spanAngleChanged(double spanAngle); 47 | void colorChanged(QColor color); 48 | void segmentCountChanged(int segemntCount); 49 | 50 | protected: 51 | bool _complete = false; 52 | double _penWidth = 20; 53 | double _startAngle = 0; 54 | double _spanAngle = 0; 55 | QColor _color = Qt::black; 56 | int _segmentCount = 64; 57 | }; 58 | } // namespace sp { 59 | -------------------------------------------------------------------------------- /include/Arrow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace sp { 6 | /** 7 | * @brief Класс для отрисовки стрелки через Scene Graph. 8 | * @note Антиалиасинг осуществляется с помощью QSurfaceFormat format; format.setSample(16). 9 | * Подробнее смотри в примере в файле main.cpp. 10 | */ 11 | class Arrow : public QQuickItem 12 | { 13 | Q_OBJECT 14 | Q_INTERFACES(QQmlParserStatus) 15 | 16 | Q_PROPERTY (double penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged) 17 | Q_PROPERTY (QColor color READ color WRITE setColor NOTIFY colorChanged) 18 | 19 | public: 20 | Arrow(QQuickItem *parent = nullptr); 21 | 22 | virtual void classBegin() override; 23 | virtual void componentComplete() override; 24 | QSGNode* updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; 25 | 26 | double penWidth() const { return _penWidth; } 27 | QColor color() const { return _color; } 28 | 29 | void setPenWidth(double penWidth); 30 | void setColor(const QColor &color); 31 | 32 | signals: 33 | void penWidthChanged(double penWidth); 34 | void colorChanged(QColor color); 35 | 36 | protected: 37 | bool _complete = false; 38 | double _penWidth = 20; 39 | QColor _color = Qt::black; 40 | int _vertexCount = 10; 41 | }; 42 | } // namespace sp { 43 | -------------------------------------------------------------------------------- /include/DeviceInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace sp { 6 | /** 7 | * @brief Класс, предоставляющий информацию об устройстве 8 | */ 9 | class DeviceInfo : public QObject 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | DeviceInfo(QObject *parent = NULL); 15 | static DeviceInfo &instance(QObject *parent = 0); 16 | 17 | /** \brief Возвращает уникальный идентефикатор устрйоства **/ 18 | Q_INVOKABLE QString deviceID(); 19 | 20 | Q_INVOKABLE float getAndroidVersion(); 21 | 22 | Q_INVOKABLE int availableRam(); 23 | 24 | //Q_INVOKABLE int totalRam(); 25 | 26 | Q_INVOKABLE void killApp(); 27 | 28 | /** @brief Возвращает высоту StasusBar'a **/ 29 | Q_INVOKABLE int statusBarHeight(); 30 | 31 | /** @brief Можно ли изменять statusBar **/ 32 | Q_INVOKABLE bool abilityChangeStatusBar(); 33 | 34 | 35 | private: 36 | static DeviceInfo _instance; 37 | }; 38 | } // namespace sp { 39 | -------------------------------------------------------------------------------- /include/DownloadFileHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Private/NetHandler.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class QFile; 11 | 12 | namespace sp { 13 | 14 | /***************************************************************************//** 15 | * @brief Обработчик запроса на скачивание файла 16 | ******************************************************************************/ 17 | class DownloadFileHandler: public NetHandler { 18 | Q_OBJECT 19 | 20 | public: 21 | DownloadFileHandler(const QUrl &url, const QFileInfo &fileInfo); 22 | 23 | virtual const QUrl& url() const override { return _url; } 24 | virtual void tuningRequest(QNetworkRequest *) override; 25 | 26 | const QFileInfo& fileInfo() const { return _fileInfo; } 27 | QFile *file() { return _file; } 28 | 29 | protected slots: 30 | virtual void onError(QNetworkReply::NetworkError) override; 31 | virtual void onDownloadProgress(quint64 bytesReceived, quint64 bytesTotal) override; 32 | virtual void onFinished() override; 33 | 34 | protected: 35 | QUrl _url; 36 | QFileInfo _fileInfo; 37 | QFile *_file; 38 | bool _needCheckAcceptRanges; 39 | }; 40 | 41 | } // namespace sp { 42 | -------------------------------------------------------------------------------- /include/ImageParallax.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace sp { 9 | 10 | class ImageParallax: public QQuickItem 11 | { 12 | Q_OBJECT 13 | Q_INTERFACES(QQmlParserStatus) 14 | 15 | Q_PROPERTY(bool isDebug READ isDebug WRITE setIsDebug) 16 | Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) 17 | Q_PROPERTY(QQuickItem* relativeItem READ relativeItem WRITE setRelativeItem NOTIFY relativeItemChanged) 18 | Q_PROPERTY(QQuickItem* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) 19 | Q_PROPERTY(bool freezed READ freezed WRITE setFreezed NOTIFY freezedChanged) 20 | Q_PROPERTY(qreal sizeMultiplier READ sizeMultiplier WRITE setSizeMultiplier NOTIFY sizeMultiplierChanged) 21 | 22 | // Поля для ImageSp 23 | Q_PROPERTY (QString source READ source WRITE setSource NOTIFY sourceChanged) 24 | Q_PROPERTY (QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged) 25 | Q_PROPERTY (bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) 26 | Q_PROPERTY (bool antialiasing READ antialiasing WRITE setAntialiasing ) 27 | Q_PROPERTY (sp::ImageSp::Status status READ status NOTIFY statusChanged) 28 | 29 | public: 30 | enum Orientation { 31 | Horizontal = 0 32 | , Vertical = 1 33 | }; 34 | 35 | Q_ENUM (Orientation) 36 | 37 | public: 38 | ImageParallax(QQuickItem *parent = nullptr); 39 | virtual void classBegin() override; 40 | virtual void componentComplete() override; 41 | 42 | //---------------------------------------------------------------------- 43 | // Get 44 | //---------------------------------------------------------------------- 45 | inline bool isDebug() const { return _isDebug; } 46 | inline bool freezed() const { return _freezed; } 47 | inline qreal sizeMultiplier() const { return _sizeMultiplier; } 48 | inline QString source() const { return _source; } 49 | inline QSize sourceSize() const { return _image->sourceSize(); } 50 | inline bool asynchronous() const { return _asynchronous; } 51 | inline Orientation orientation() const { return _orientation; } 52 | inline sp::ImageSp::Status status() const { return _image->status(); } 53 | inline QQuickItem* relativeItem () const { return _relativeItem; } 54 | inline QQuickItem* delegate () const { return _delegate; } 55 | 56 | //---------------------------------------------------------------------- 57 | // Set 58 | //---------------------------------------------------------------------- 59 | void setOrientation(Orientation orientation); 60 | void setIsDebug(bool isDebug); 61 | void setRelativeItem(QQuickItem* relativeItem); 62 | void setDelegate(QQuickItem* delegate); 63 | void setSource(const QString &source); 64 | void setAsynchronous (bool asynchronous); 65 | void setAntialiasing (bool antialiasing); 66 | void setFreezed (bool freezed); 67 | void setSizeMultiplier (qreal sizeMultiplier); 68 | 69 | private slots: 70 | void updateImageSize(); 71 | void updateImagePosition(); 72 | 73 | signals: 74 | void orientationChanged(const Orientation orientation); 75 | void delegateChanged(const QQuickItem* delegate); 76 | void relativeItemChanged(const QQuickItem* relativeItem); 77 | void freezedChanged(bool freezed); 78 | void sourceChanged(const QString&); 79 | void sourceSizeChanged(const QSize&); 80 | void statusChanged(sp::ImageSp::Status); 81 | void asynchronousChanged(bool); 82 | void sizeMultiplierChanged(qreal sizeMultiplier); 83 | 84 | private: 85 | QString _source; 86 | QQuickItem* _delegate = nullptr; 87 | QQuickItem* _relativeItem = nullptr; 88 | ImageSp *_image; 89 | qreal _sizeMultiplier = 1.5; 90 | Orientation _orientation = Orientation::Vertical; 91 | sp::ImageSp::Status _status = sp::ImageSp::Null; 92 | bool _isDebug = false; 93 | bool _blur = false; 94 | bool _asynchronous = true; 95 | bool _imageInit = false; 96 | bool _freezed = false; 97 | }; 98 | } 99 | -------------------------------------------------------------------------------- /include/ImageSaverSp.h: -------------------------------------------------------------------------------- 1 | #ifndef ImageSaverSp_H 2 | #define ImageSaverSp_H 3 | 4 | #include 5 | 6 | namespace sp { 7 | class ImageSaverSp: public QObject 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | ImageSaverSp(QObject *parent=0); 13 | 14 | Q_INVOKABLE QString save (const QString &imageUrl, const QString &albumName); 15 | Q_INVOKABLE bool checkFile(const QString &imageUrl, const QString &albumName); 16 | 17 | signals: 18 | void successSave(); 19 | void cancelSave(); 20 | void noAccess(); 21 | 22 | private: 23 | QString saveImage(const QString &imageUrl, const QString &albumName); 24 | void androidUpdateGalleryApp(const QString& filePath); 25 | QString getImageNameFromURL(const QString& imageURL); 26 | 27 | private: 28 | const QString pictureLocation; 29 | }; 30 | } 31 | 32 | #endif // ImageSaverSp_H 33 | -------------------------------------------------------------------------------- /include/ImageSp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Private/ImageSpLoader.h" 4 | #include "Private/ImageSpNode.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace sp { 10 | /** 11 | * @brief Класс для отрисовки изображения с закруглёнными краями через Scene Graph. 12 | */ 13 | class ImageSp: public QQuickItem 14 | { 15 | Q_OBJECT 16 | Q_INTERFACES(QQmlParserStatus) 17 | 18 | Q_PROPERTY (QString source READ source WRITE setSource NOTIFY sourceChanged) 19 | Q_PROPERTY (float radius READ radius WRITE setRadius NOTIFY radiusChanged) 20 | Q_PROPERTY (QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged) 21 | Q_PROPERTY (FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) 22 | Q_PROPERTY (Status status READ status NOTIFY statusChanged) 23 | Q_PROPERTY (bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) 24 | 25 | /** @property mipmap 26 | * @brief Флаг mipmap-фильтрации изображения, оно же более качественное масштабирование. 27 | * @note На iOS может глючить, нужно проверить 28 | */ 29 | Q_PROPERTY (bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged) 30 | 31 | Q_PROPERTY (HorizontalAlignment horizontalAlignment READ horizontalAlignment WRITE setHorizontalAlignment NOTIFY horizontalAlignmentChanged) 32 | Q_PROPERTY (VerticalAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged) 33 | 34 | public: 35 | enum FillMode { 36 | Stretch = 0 37 | ,PreserveAspectFit = 1 38 | ,PreserveAspectCrop = 2 39 | ,Pad = 6 40 | ,Parallax = 8 41 | }; 42 | 43 | /** @brief Горизонтальное выравнивание */ 44 | enum HorizontalAlignment { 45 | AlignLeft = 1 46 | , AlignRight = 2 47 | , AlignHCenter = 4 48 | }; 49 | 50 | /** @brief Вертикальное выравнивание */ 51 | enum VerticalAlignment { 52 | AlignTop = 32 53 | , AlignBottom = 64 54 | , AlignVCenter = 128 55 | }; 56 | 57 | enum Status { 58 | Null = 0 59 | , Ready = 1 60 | , Loading = 2 61 | , Error = 3 62 | }; 63 | 64 | Q_ENUM (FillMode) 65 | Q_ENUM (HorizontalAlignment) 66 | Q_ENUM (VerticalAlignment) 67 | Q_ENUM (Status) 68 | 69 | public: 70 | ImageSp(QQuickItem *parent = nullptr); 71 | ~ImageSp(); 72 | 73 | virtual void classBegin() override; 74 | virtual void componentComplete() override; 75 | QSGNode* updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; 76 | 77 | const QString& source() const { return _source; } 78 | float radius() const { return _radius; } 79 | QSize sourceSize() const { return _image->size(); } 80 | FillMode fillMode() const { return _fillMode; } 81 | Status status() const { return _status; } 82 | bool asynchronous() const { return _asynchronous; } 83 | bool mipmap() const { return _mipmap; } 84 | HorizontalAlignment horizontalAlignment() const { return _horizontalAlignment; } 85 | VerticalAlignment verticalAlignment() const { return _verticalAlignment; } 86 | 87 | void setSource(const QString &source); 88 | void setRadius(float radius); 89 | void setFillMode(FillMode fillMode); 90 | void setAsynchronous (bool asynchronous); 91 | void setMipmap(bool mipmap); 92 | void setHorizontalAlignment (HorizontalAlignment horizontalAlignment); 93 | void setVerticalAlignment (VerticalAlignment verticalAlignment); 94 | 95 | signals: 96 | void sourceChanged(const QString&); 97 | void radiusChanged(float); 98 | void sourceSizeChanged(const QSize&); 99 | void statusChanged(Status); 100 | void fillModeChanged(FillMode); 101 | void asynchronousChanged(bool); 102 | void mipmapChanged(bool); 103 | void horizontalAlignmentChanged(HorizontalAlignment); 104 | void verticalAlignmentChanged(VerticalAlignment); 105 | 106 | protected slots: 107 | void onImageSpLoaded (const QString &source, sp::ImageWeakPtr image); 108 | void onImageSpError (const QString &source, sp::ImageWeakPtr image, const QString &reason); 109 | 110 | protected: 111 | struct Coefficients { 112 | float x,y,w,h; 113 | float tx,ty,tw,th; 114 | }; 115 | 116 | Coefficients coefficientsPad() const; 117 | Coefficients coefficientsStretch() const; 118 | Coefficients coefficientsPreserveAspectFit() const; 119 | Coefficients coefficientsPreserveAspectCrop() const; 120 | Coefficients coefficientsParallax() const; 121 | 122 | protected: 123 | bool _completed = false; 124 | int _vertexAtCorner = 20; 125 | int _segmentCount = 4*_vertexAtCorner+3; 126 | 127 | ImageSpNode *_node; 128 | ImageSharedPtr _image; 129 | bool _imageUpdated = false; 130 | 131 | QString _source; 132 | float _radius = 0.0; 133 | Status _status = Null; 134 | bool _asynchronous = true; 135 | bool _mipmap = true; 136 | FillMode _fillMode = PreserveAspectCrop; 137 | HorizontalAlignment _horizontalAlignment = AlignHCenter; 138 | VerticalAlignment _verticalAlignment = AlignVCenter; 139 | }; 140 | } // namespace sp { 141 | -------------------------------------------------------------------------------- /include/KeyboardSp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace sp { 6 | class KeyboardSp: public QObject 7 | { 8 | Q_OBJECT 9 | Q_PROPERTY (bool visible READ visible NOTIFY visibleChanged) 10 | Q_PROPERTY (int height READ height NOTIFY heightChanged) 11 | 12 | public: 13 | static KeyboardSp& instance(); 14 | static void sendVisibleChanged(bool visible, int height); 15 | 16 | Q_INVOKABLE void show(); 17 | Q_INVOKABLE void hide(); 18 | 19 | bool visible() const { return _visible; } 20 | int height() const { return _height; } 21 | 22 | protected: 23 | KeyboardSp(); 24 | 25 | signals: 26 | void visibleChanged(bool visible); 27 | void heightChanged(int height); 28 | 29 | protected: 30 | int _height; 31 | bool _visible = false; 32 | }; 33 | } // namespace k12 { 34 | -------------------------------------------------------------------------------- /include/LogSp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef SP_ALEUS 9 | #define LOG_ALEUS(str) qDebug() << str 10 | #else 11 | #define LOG_ALEUS(str) ; 12 | #endif 13 | 14 | #ifdef SP_VONABIRG 15 | #define LOG_VONABIRG(str) qDebug() << str 16 | #else 17 | #define LOG_VONABIRG(str) 18 | #endif 19 | #define LOG_V LOG_VONABIRG 20 | 21 | // Depricated!!! 22 | #define LOG_DEBUG(str) qDebug() << str 23 | #define LOG_INFO(str) qDebug() << str 24 | #define LOG_ERROR(str) qWarning() << str 25 | #define LOG_WARN(str) qWarning() << str 26 | #define LOG_FATAL(str) qFatal(str.toLocal8Bit()) 27 | 28 | namespace sp { 29 | /** 30 | * @brief Класс логирования отладки, ошибок, предупреждений и т.п. Имеет интерфейс для C++ (signleton) и QML. 31 | */ 32 | class Log: public QObject 33 | { 34 | Q_OBJECT 35 | 36 | Log(QObject *parent = NULL); 37 | 38 | public: 39 | static Log& instance(); 40 | 41 | /** @brief Выводит в лог строку str. */ 42 | // Depricated!!! 43 | Q_INVOKABLE static void debug (const QString &str); 44 | Q_INVOKABLE static void info (const QString &str); 45 | Q_INVOKABLE static void error (const QString &str); 46 | Q_INVOKABLE static void warning (const QString &str); 47 | Q_INVOKABLE static void fatal (const QString &str); 48 | Q_INVOKABLE void aleus (const QString &str); 49 | Q_INVOKABLE void v (const QString &str); 50 | 51 | /// @brief Возвращает архив логирования 52 | Q_INVOKABLE QString logArchive() const; 53 | 54 | static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message); 55 | 56 | signals: 57 | void logArchiveChanged(); 58 | 59 | private: 60 | inline void toArchive(const QString &str); 61 | 62 | private: 63 | static QStringList _logArchive; 64 | static Log _instance; 65 | static QTime _time; 66 | }; // class Log: public QObject 67 | } // namespace sp { 68 | -------------------------------------------------------------------------------- /include/Net.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Private/NetHandler.h" 4 | #include "DownloadFileHandler.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace sp { 16 | 17 | /***************************************************************************//** 18 | * @brief Синглетон для работы с сетью. 19 | * 20 | * Класс отправляет запросы на сервер и в случае обрыва и восстановления связи 21 | * перепосылает неудавшиеся запросы. Класс является потокобезопасным и обработку 22 | * запросов ведёт в параллельном потоке. 23 | * 24 | * Для обработки запросов применяются дочерний классы NetHandler'а. При успешном 25 | * выполнении запроса посылается сигнал NetHandler::finished. В случае ошибки 26 | * отправляется сигнал NetHandler::error с аргументом needRetry. Если needRetry 27 | * выставлен в true, то запрос будет заново отправлен в свою очередь. 28 | * 29 | * Для отправки запроса нужно послать сигнал makeRequest с указателем на обработчик 30 | * запросов (дочерний класс NetHandler). Также имеютсю удобные методы для загрузки 31 | * файлов - downloadFile. 32 | * 33 | * Метод abortRequest останавливает обработку запроса. 34 | * 35 | * @note Временем жизни объектов NetHandler класс Net не управляет. Поэтому можно 36 | * удалять обработчики только после сигнала NetHandler::finished или 37 | * NetHandler::error(false). Чтобы удалить NetHandler в любое другое время нужно 38 | * запустить метод abortRequest. 39 | * 40 | * @code 41 | * DownloadFileHandler *handler = Net::downloadFile("http://download.downloadmaster.ru/dm/dmaster.exe"); 42 | * QObject::connect(handler, &DownloadFileHandler::finished , [=](){ 43 | * LOG_ALEUS("main.cpp: Загрузка завершена" << handler->file()->size()); 44 | * } ); 45 | ******************************************************************************/ 46 | class Net: public QObject { 47 | Q_OBJECT 48 | 49 | Net(); 50 | public: 51 | static Net& instance(); 52 | 53 | static DownloadFileHandler* downloadFile(const QString &url, const QString &fileName); 54 | static DownloadFileHandler* downloadFile(const QString &url, const QFileInfo &fileName); 55 | static DownloadFileHandler* downloadFile(const QString &url); 56 | 57 | static QThread* thread() { return &instance()._thread; } 58 | 59 | signals: 60 | /// @brief Посылает запрос и передаёт его обработку handler'у 61 | void makeRequest(NetHandler *handler); 62 | 63 | /// @brief Останавливает обработку запроса, соответствующего handler'у 64 | void abortRequest(NetHandler *handler); 65 | 66 | protected slots: 67 | void onNetworkAccessibilityChanged(QNetworkAccessManager::NetworkAccessibility accessible); 68 | void onMakeRequest(NetHandler *handler = nullptr); 69 | void onNetHandlerFinished(); 70 | void onNetHandlerError(bool needRetry); 71 | void onAbortRequest(NetHandler *handler); 72 | 73 | protected: 74 | QNetworkAccessManager _nam; 75 | QThread _thread; 76 | QSet _activeHandler; // Активные сетевые запросы. Максимальное количество - MAX_ACTIVE_HANDLERS 77 | QQueue _handlersQueue; // Очередь обработчиков сетевых запросов 78 | // TODO Для очереди приоритетов нужно сделать отдельный контейнер 79 | }; 80 | }; 81 | -------------------------------------------------------------------------------- /include/Settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef SETTINGS_H 3 | #define SETTINGS_H 4 | 5 | #include 6 | #include 7 | 8 | namespace sp { 9 | /***************************************************************************//** 10 | * @brief Обёртка для просто использования настроек в C++ и QML. 11 | * 12 | * @details 13 | ******************************************************************************/ 14 | class Settings : public QObject 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | static Settings& instance(); 20 | 21 | /** 22 | * @brief Устанавливет значение переменной в настройках. 23 | * 24 | * @param name название переменной 25 | * @param value устанавливаемое значение 26 | */ 27 | Q_INVOKABLE static 28 | void set(const QString &key, const QVariant &value); 29 | 30 | /** 31 | * @brief Возвращает значение переменной из настроек. 32 | * 33 | * @param name название 34 | * @param defaultValue значение по умолчанию, возвращаемое, если переменной нет в настройках 35 | */ 36 | Q_INVOKABLE static 37 | QVariant get(const QString &key, const QVariant &defaultValue = QVariant()); 38 | 39 | private: 40 | explicit Settings(); 41 | ~Settings(); 42 | 43 | void onSet(const QString &key, const QVariant &value); 44 | QVariant onGet(const QString &key, const QVariant &defaultValue) const; 45 | 46 | private: 47 | QSettings _settings; 48 | }; 49 | } // namespace sp 50 | 51 | #endif // SETTINGS_H 52 | -------------------------------------------------------------------------------- /include/Shadow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace sp { 8 | /** 9 | * @brief Компонент отрисовки тени 10 | */ 11 | class Shadow : public QQuickPaintedItem 12 | { 13 | Q_OBJECT 14 | Q_INTERFACES(QQmlParserStatus) 15 | Q_PROPERTY (QColor color READ color WRITE setColor NOTIFY colorChanged) 16 | 17 | // TODO добавить Enum какая это тень 18 | 19 | public: 20 | Shadow(QQuickItem *parent = nullptr); 21 | 22 | virtual void classBegin() override; 23 | virtual void componentComplete() override; 24 | virtual void paint(QPainter *painter) override; 25 | 26 | QColor color() const { return _color; } 27 | 28 | void setColor(const QColor &color); 29 | 30 | signals: 31 | void colorChanged(QColor color); 32 | 33 | protected: 34 | bool _complete = false; 35 | QColor _color = QColor("#15000000"); 36 | }; 37 | } // namespace sp { 38 | -------------------------------------------------------------------------------- /include/SpApplicationPrototype.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #if defined(qApp) 7 | #undef qApp 8 | #endif 9 | #define qApp (static_cast(QCoreApplication::instance())) 10 | 11 | namespace sp { 12 | /** 13 | * @brief Базовый класс приложения для проектов SP. 14 | * Регистрирует компоненты и объекты QML. 15 | */ 16 | class SpApplicationPrototype : public QGuiApplication 17 | { 18 | public: 19 | SpApplicationPrototype(int &argc, char **argv 20 | ,const QString &title 21 | ,int width = 230, int height = 400); 22 | 23 | int exec(const QUrl &source = QUrl("qrc:/Root.qml")); 24 | 25 | QQuickView *view() { return &_view; } 26 | 27 | /** @brief Возвращает количество пикселей в миллиметре. */ 28 | double mm() const { return _mm; } 29 | 30 | protected: 31 | QQuickView _view; 32 | double _mm; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /include/private/ImageSpLoader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace sp { 10 | typedef QSharedPointer ImageSharedPtr; 11 | typedef QWeakPointer ImageWeakPtr; 12 | 13 | /***************************************************************************//** 14 | * @brief Класс-singleton загрузщика изображений для ImageSp. 15 | * 16 | * @note Работает в параллельном потоке. 17 | ******************************************************************************/ 18 | class ImageSpLoader : public QObject { 19 | Q_OBJECT 20 | 21 | ImageSpLoader (); 22 | public: 23 | static ImageSpLoader& instance(); 24 | 25 | public slots: 26 | /// @brief Загружает изображение синхронно. 27 | void get (const QString &source, ImageSharedPtr image); 28 | 29 | signals: 30 | /// @brief Загружает изображение ассинхронно. 31 | void loadTo(const QString &source, ImageSharedPtr image); 32 | 33 | /// @brief [signal] Сигнал, уведомляющий о загрузке изображения. 34 | void loaded(const QString &source, ImageWeakPtr image); 35 | 36 | /// @brief [signal] Сигнал об ошибке при загрузке изображения. 37 | void error(const QString &source, ImageWeakPtr image, const QString &reason); 38 | 39 | private: 40 | QThread _thread; 41 | }; // class ImageSpLoader 42 | } // namespace sp { 43 | 44 | Q_DECLARE_METATYPE(sp::ImageSharedPtr) 45 | Q_DECLARE_METATYPE(sp::ImageWeakPtr) 46 | -------------------------------------------------------------------------------- /include/private/ImageSpNode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace sp { 7 | class ImageSpNode: public QSGGeometryNode 8 | { 9 | public: 10 | ImageSpNode(int vertexAtCorner = 20); 11 | ~ImageSpNode(); 12 | 13 | void setImage(const QImage &image); 14 | void setMipmap(bool mipmap); 15 | 16 | protected: 17 | int _vertexAtCorner; 18 | int _segmentCount; 19 | bool _mipmap = true; 20 | }; 21 | } // namespace sp { 22 | -------------------------------------------------------------------------------- /include/private/NetHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace sp { 8 | 9 | /***************************************************************************//** 10 | * @brief Абстрактный обработчик сетевого запроса 11 | ******************************************************************************/ 12 | class NetHandler: public QObject { 13 | Q_OBJECT 14 | 15 | protected: 16 | NetHandler(); 17 | void setReply(QNetworkReply *reply); 18 | QNetworkReply* reply() { return _reply; } 19 | 20 | public: 21 | virtual const QUrl& url() const = 0; 22 | 23 | /// @brief Настравивает запрос перед отправкой 24 | virtual void tuningRequest(QNetworkRequest *) {} 25 | 26 | signals: 27 | /// @brief [signal] Сигнал об успешном выполенении сетевого запроса 28 | void finished(); 29 | 30 | /// @brief [signal] Сигнал об ошибке запроса 31 | /// @param needRetry - нужен повтор запроса, после востановления связи 32 | void error(bool needRetry); 33 | 34 | protected slots: 35 | virtual void onDownloadProgress(quint64 bytesReceived, quint64 bytesTotal) = 0; 36 | virtual void onFinished() = 0; 37 | virtual void onError(QNetworkReply::NetworkError) = 0; 38 | 39 | protected: 40 | QNetworkReply *_reply; 41 | 42 | friend class Net; 43 | }; 44 | 45 | } // namespace sp { 46 | -------------------------------------------------------------------------------- /qml/ActionBar.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "./" 4 | import "../" 5 | 6 | Rectangle { 7 | id: actionBar 8 | 9 | /// @brief Текст основной надписи. 10 | property alias title: title.text 11 | 12 | /// @brief Текст подстрочной надписи. 13 | property alias subTitle: subTitle.text 14 | 15 | /// @brief Цвет надписи. 16 | property alias titleColor: title.color 17 | 18 | /// @brief Варавнивание надписи. 19 | property alias titleAlignment: title.horizontalAlignment 20 | 21 | /// @brief Компонент левой кнопки 22 | property alias leftButton: leftButton 23 | 24 | /// @brief Компонент правой кнопки 25 | property alias rightButton: rightButton 26 | 27 | /// @brief Компонент второй строки ActionBar'а 28 | property alias secondLine: secondLine.sourceComponent 29 | 30 | /// @brief Флаг, указывающий на прозрачность. 31 | property bool transparent: false 32 | 33 | property alias shadowVisible: shadow.visible 34 | 35 | color: transparent ? "transparent" : Consts.actionBarColor 36 | height: firstLine.height + secondLine.height 37 | width: parent.width 38 | 39 | Rectangle { 40 | anchors { 41 | left: parent.left 42 | right: parent.right 43 | bottom: parent.top 44 | } 45 | height: Consts.statusBarHeight 46 | color: parent.color 47 | } 48 | 49 | //-------------------------------------------------------------------------- 50 | Item { 51 | id: firstLine 52 | 53 | width: parent.width 54 | height: Consts.actionBarHeight 55 | anchors.top: parent.top 56 | 57 | //---------------------------------------------------------------------- 58 | // Левая кнопка 59 | //---------------------------------------------------------------------- 60 | Loader { 61 | id: leftButton 62 | anchors { 63 | top: parent.top 64 | left: parent.left 65 | bottom: parent.bottom 66 | } 67 | } 68 | 69 | //---------------------------------------------------------------------- 70 | // Заголовок 71 | //---------------------------------------------------------------------- 72 | Item { 73 | id: titlesContainer 74 | 75 | anchors { 76 | top: parent.top 77 | left: leftButton.right 78 | right: rightButton.left 79 | bottom: parent.bottom 80 | } 81 | 82 | Column { 83 | anchors { 84 | left: parent.left 85 | right: parent.right 86 | margins: Consts.spacing 87 | leftMargin: leftButton.width ? 0 : Consts.spacing 88 | verticalCenter: parent.verticalCenter 89 | } 90 | 91 | Text { 92 | id: title 93 | 94 | color: "#555555" 95 | elide: Text.ElideRight 96 | wrapMode: Text.NoWrap 97 | minimumPixelSize: 1 98 | fontSizeMode: Text.HorizontalFit 99 | height: contentHeight 100 | font { 101 | pixelSize: Consts.fontTitle 102 | letterSpacing: Consts.fontTitleLetterSpacing 103 | weight: Font.Bold 104 | } 105 | anchors { 106 | left: parent.left 107 | right: parent.right 108 | } 109 | } 110 | 111 | Text { 112 | id: subTitle 113 | 114 | color: title.color 115 | horizontalAlignment: title.horizontalAlignment 116 | elide: Text.ElideRight 117 | font { 118 | pixelSize: Consts.fontSmall 119 | letterSpacing: Consts.fontSmallLetterSpacing 120 | } 121 | anchors { 122 | left: parent.left 123 | right: parent.right 124 | } 125 | height: text === "" 126 | ? 0 127 | : contentHeight 128 | } 129 | } 130 | } // Item { id: titlesContainer 131 | 132 | //---------------------------------------------------------------------- 133 | // Правая кнопка 134 | //---------------------------------------------------------------------- 135 | Loader { 136 | id: rightButton 137 | anchors { 138 | top: parent.top 139 | right: parent.right 140 | bottom: parent.bottom 141 | } 142 | } 143 | } // Item { id: firstLine 144 | 145 | //-------------------------------------------------------------------------- 146 | Loader { 147 | id: secondLine 148 | 149 | anchors.top: firstLine.bottom 150 | width: parent.width 151 | } 152 | 153 | //-------------------------------------------------------------------------- 154 | ShadowBottom { 155 | id: shadow 156 | 157 | anchors.top: parent.bottom 158 | visible: !transparent 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /qml/ActionBarImage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "./" 4 | import "../" 5 | 6 | Rectangle { 7 | id: actionBarImage 8 | 9 | property alias shift: _p.shift 10 | 11 | /// @brief Источник изображения 12 | property alias source: image.source 13 | 14 | /// @brief Высота изображения 15 | property double imageHeight: 30*mm 16 | 17 | /// @brief Текст основной надписи. 18 | property alias title: title.text 19 | 20 | /// @brief Текст подстрочной надписи. 21 | property alias subTitle: subTitle.text 22 | 23 | /// @brief Цвет надписи. 24 | property alias titleColor: title.color 25 | 26 | /// @brief Варавнивание надписи. 27 | property alias titleAlignment: title.horizontalAlignment 28 | 29 | /// @brief Компонент левой кнопки 30 | property alias leftButton: leftButton.sourceComponent 31 | 32 | /// @brief Координата y прокручиваемого содержимого 33 | property double contentY: 0 34 | 35 | color: Consts.actionBarColor 36 | width: parent.width 37 | height: Math.max (Consts.actionBarHeight, imageHeight-contentY) 38 | 39 | //-------------------------------------------------------------------------- 40 | Image { 41 | id: image 42 | 43 | fillMode: Image.PreserveAspectCrop 44 | anchors.fill: parent 45 | opacity: 1-_p.shift 46 | } 47 | 48 | //-------------------------------------------------------------------------- 49 | Loader { 50 | id: leftButton 51 | anchors { 52 | top: parent.top 53 | left: parent.left 54 | } 55 | height: Consts.actionBarHeight 56 | } 57 | 58 | //-------------------------------------------------------------------------- 59 | Item { 60 | id: titlesContainer 61 | 62 | anchors { 63 | left: parent.left 64 | right: parent.right 65 | bottom: parent.bottom 66 | leftMargin: Consts.margin + (leftButton.width-Consts.margin)*_p.shift 67 | } 68 | height: Consts.actionBarHeight 69 | 70 | Column { 71 | anchors { 72 | left: parent.left 73 | right: parent.right 74 | margins: Consts.spacing 75 | leftMargin: leftButton.width ? 0 : Consts.spacing 76 | verticalCenter: parent.verticalCenter 77 | } 78 | 79 | TextNormal { 80 | id: title 81 | 82 | color: "#555555" 83 | elide: Text.ElideRight 84 | wrapMode: Text.NoWrap 85 | font.weight: Font.Bold 86 | 87 | anchors { 88 | left: parent.left 89 | right: parent.right 90 | } 91 | } 92 | 93 | TextSmall { 94 | id: subTitle 95 | 96 | color: title.color 97 | horizontalAlignment: title.horizontalAlignment 98 | elide: Text.ElideRight 99 | wrapMode: Text.NoWrap 100 | anchors { 101 | left: parent.left 102 | right: parent.right 103 | } 104 | height: text === "" 105 | ? 0 106 | : contentHeight 107 | } 108 | } 109 | } 110 | 111 | //-------------------------------------------------------------------------- 112 | ShadowTop { 113 | anchors.bottom: parent.bottom 114 | opacity: 1-_p.shift 115 | } 116 | 117 | ShadowBottom { 118 | anchors.top: parent.bottom 119 | opacity: _p.shift 120 | } 121 | 122 | //-------------------------------------------------------------------------- 123 | Item { 124 | id: _p 125 | 126 | readonly property double shift: 1 -(actionBarImage.height - Consts.actionBarHeight)/(imageHeight - Consts.actionBarHeight) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /qml/BackButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import SP 1.0 3 | 4 | import "./" 5 | import "../" 6 | 7 | //------------------------------------------------------------------------------ 8 | // @brief Кнопка назад 9 | //------------------------------------------------------------------------------ 10 | MaterialButton { 11 | id: backButton 12 | 13 | property string arrowColor: "white" 14 | property real yShift: 0 15 | property bool useYShift: true 16 | 17 | color: "transparent" 18 | border.width: 0 19 | width: height-2*Consts.spacing 20 | height: Consts.actionBarHeight 21 | radius: height/2 22 | pressedColor: "black" 23 | 24 | Arrow { 25 | id: arrow 26 | 27 | readonly property int originalHeight: isDesktop 28 | ? Math.ceil(2.7*mm) 29 | : Math.ceil(2.5*mm) 30 | height: originalHeight % 2 === 0 31 | ? originalHeight 32 | : originalHeight + 1 33 | width: height 34 | color: "white" 35 | penWidth: Consts.lineWidth 36 | antialiasing: true 37 | x: Math.ceil((parent.width-width)/2) 38 | y: useYShift 39 | ? Math.round((parent.height-height)/2) + yShift 40 | : Math.round((parent.height-height)/2) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /qml/ButtonPrototype.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "./" 4 | import "../" 5 | 6 | //------------------------------------------------------------------------------ 7 | // @brief Протип кнопки. Обрабатывает сигналы нажатия, и т.п. 8 | // @note Необходим для обхода узких мест, к примеру, в MouseArea нельзя послать 9 | // сигнал pressed() программно. 10 | //------------------------------------------------------------------------------ 11 | Item { 12 | id: _buttonPrototype 13 | 14 | readonly property alias isPressed: mouseArea.pressed 15 | property double pressedX: 0 16 | property double pressedY: 0 17 | property double margins: -Consts.spacing 18 | 19 | signal pressed 20 | signal released 21 | signal clicked 22 | signal pressedAndHold 23 | 24 | //-------------------------------------------------------------------------- 25 | MouseArea { 26 | id: mouseArea 27 | 28 | anchors { 29 | fill: parent 30 | margins: _buttonPrototype.margins 31 | } 32 | enabled: parent.enabled 33 | 34 | onPressed: { 35 | pressedX = Math.min(Math.max(mouse.x + margins, 0), _buttonPrototype.width); 36 | pressedY = Math.min(Math.max(mouse.y + margins, 0), _buttonPrototype.height); 37 | 38 | _buttonPrototype.pressed(); 39 | } 40 | 41 | onReleased: { 42 | _buttonPrototype.released(); 43 | } 44 | 45 | onClicked: { 46 | _buttonPrototype.clicked(); 47 | } 48 | 49 | onPressAndHold: { 50 | _buttonPrototype.pressedAndHold(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /qml/ConstsPrototype.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import SP 1.0 3 | 4 | Item { 5 | property double margin: Math.ceil(2.0*mm) 6 | property double marginBig: Math.ceil(3.4*mm) 7 | property double spacing: Math.ceil(1*mm) 8 | property double marginCorner: Math.ceil(10*mm) 9 | property double fadeWidth: Math.ceil(5*mm) 10 | property double radius: Math.ceil(1*mm) 11 | property double lineWidth: Math.ceil(0.3*mm) 12 | property double borderWidth: Math.ceil(0.3*mm) 13 | 14 | property color gray: "#c0c0c0" 15 | property color darkGray: "#808080" 16 | property color lightGray: "#f0f0f0" 17 | property color green: "#71ad6f" 18 | property color lightGreen: "#d1ecd0" 19 | property color blue: "#0096d5" 20 | property color buttonActiveColor : "#72f7d6" 21 | property color buttonPassiveColor: "#c8e6df" 22 | property color borderColor: "#d9d9d9" 23 | property color shadowColor: "#dadada" 24 | property color backgroundColor: "#f0f0f0" 25 | 26 | //-------------------------------------------------------------------------- 27 | // Шрифты 28 | //-------------------------------------------------------------------------- 29 | //property alias mainFont: _mainFont 30 | property alias fontMetricsBig : _fontMetricsBig 31 | property alias fontMetricsTitle : _fontMetricsTitle 32 | property alias fontMetricsNormal: _fontMetricsNormal 33 | property alias fontMetricsSmall : _fontMetricsSmall 34 | 35 | property double fontBig : Math.ceil(4.5*mm) 36 | property double fontTitle : Math.ceil(3.2*mm) 37 | property double fontNormal: Math.ceil(2.7*mm) 38 | property double fontSmall : Math.ceil(2*mm) 39 | property double fontTiny : Math.ceil(1*mm) 40 | property double fontBigHeight : Math.ceil(1.73 * fontBig) 41 | property double fontTitleHeight : Math.ceil(1.73 * fontTitle) 42 | property double fontNormalHeight: Math.ceil(1.73 * fontNormal) 43 | property double fontSmallHeight : Math.ceil(1.73 * fontSmall) 44 | property double fontTinyHeight : Math.ceil(1.73 * fontTiny) 45 | property double fontBigLetterSpacing : Math.ceil(0.4*mm) 46 | property double fontTitleLetterSpacing : Math.ceil(0.2*mm) 47 | property double fontNormalLetterSpacing: Math.ceil(0.2*mm) 48 | property double fontSmallLetterSpacing : Math.ceil(0.1*mm) 49 | 50 | //-------------------------------------------------------------------------- 51 | // ActionBar 52 | //-------------------------------------------------------------------------- 53 | readonly property int _actionBarHeight: Qt.platform.os === "ios" 54 | ? 7*mm 55 | : 9.5*mm 56 | 57 | // Высота должна содержать нечётное количество пикселей. 58 | property int actionBarHeight: _actionBarHeight % 2 === 0 59 | ? _actionBarHeight 60 | : _actionBarHeight + 1 61 | property color actionBarColor: "#7395FA" 62 | property int statusBarHeight: deviceInfo.statusBarHeight() 63 | 64 | //-------------------------------------------------------------------------- 65 | // Drawer 66 | //-------------------------------------------------------------------------- 67 | property double drawerWidth: 40*mm 68 | property double drawerDragWidth: 2*mm 69 | 70 | //FontLoader { id: _mainFont; source: "qrc:/fonts/CTCSplashRounded.otf" } 71 | FontMetrics { 72 | id: _fontMetricsBig 73 | //font.family: _mainFont.name 74 | font.pixelSize: fontBig 75 | } 76 | FontMetrics { 77 | id: _fontMetricsTitle 78 | //font.family: _mainFont.name 79 | font.pixelSize: fontTitle 80 | } 81 | FontMetrics { 82 | id: _fontMetricsNormal 83 | //font.family: _mainFont.name 84 | font.pixelSize: fontNormal 85 | } 86 | FontMetrics { 87 | id: _fontMetricsSmall 88 | //font.family: _mainFont.name 89 | font.pixelSize: fontSmall 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /qml/Counter.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import SP 1.0 3 | import "../" 4 | 5 | //------------------------------------------------------------------------------ 6 | // Числовой счётчик. Анимирует увеличение и уменьшение числа. 7 | //------------------------------------------------------------------------------ 8 | Item { 9 | id: _counter 10 | 11 | property int value 12 | 13 | width: main.width + _p.last.width 14 | height: first.height 15 | clip: true 16 | 17 | QtObject { 18 | id: _p 19 | 20 | property int lastValue: value 21 | property Text last: first 22 | property Text next: second 23 | } 24 | 25 | //-------------------------------------------------------------------------- 26 | Text { 27 | id: main 28 | 29 | text: value 30 | font.pixelSize: Consts.fontNormal 31 | } 32 | 33 | //-------------------------------------------------------------------------- 34 | Text { 35 | id: first 36 | 37 | font.pixelSize: Consts.fontNormal 38 | anchors.left: main.right 39 | } 40 | 41 | //-------------------------------------------------------------------------- 42 | Text { 43 | id: second 44 | 45 | font.pixelSize: Consts.fontNormal 46 | y: parent.height 47 | anchors.left: main.right 48 | } 49 | 50 | //-------------------------------------------------------------------------- 51 | SequentialAnimation { 52 | id: decreaseAnimation 53 | 54 | ParallelAnimation { 55 | NumberAnimation { 56 | target: _p.last 57 | property: "y" 58 | from: 0 59 | to: _counter.height 60 | duration: 100 61 | } 62 | NumberAnimation { 63 | target: _p.next 64 | property: "y" 65 | from: -_counter.height 66 | to: 0 67 | duration: 100 68 | } 69 | } 70 | PropertyAction { 71 | target: _p 72 | property: "last" 73 | value: _p.last === first ? second : first 74 | } 75 | PropertyAction { 76 | target: _p 77 | property: "next" 78 | value: _p.next === first ? second : first 79 | } 80 | } 81 | 82 | //-------------------------------------------------------------------------- 83 | SequentialAnimation { 84 | id: increaseAnimation 85 | 86 | ParallelAnimation { 87 | NumberAnimation { 88 | target: _p.last 89 | property: "y" 90 | from: 0 91 | to: -_counter.height 92 | duration: 100 93 | } 94 | NumberAnimation { 95 | target: _p.next 96 | property: "y" 97 | from: _counter.height 98 | to: 0 99 | duration: 100 100 | } 101 | } 102 | PropertyAction { 103 | target: _p 104 | property: "last" 105 | value: _p.last === first ? second : first 106 | } 107 | PropertyAction { 108 | target: _p 109 | property: "next" 110 | value: _p.next === first ? second : first 111 | } 112 | } 113 | 114 | //-------------------------------------------------------------------------- 115 | onValueChanged: { 116 | if (_p.lastValue === value) { 117 | return; 118 | } 119 | 120 | if (!increaseAnimation.running && !decreaseAnimation.running) { 121 | var lastValueText = _p.lastValue.toString(); 122 | var valueText = value.toString(); 123 | 124 | if (_p.lastValue > value) { 125 | for (var i=1; i value) { 138 | decreaseAnimation.start(); 139 | } else { 140 | increaseAnimation.start(); 141 | } 142 | } else { 143 | _p.lastValue = value; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /qml/Debug.qml: -------------------------------------------------------------------------------- 1 | pragma Singleton 2 | 3 | import QtQuick 2.8 4 | 5 | Item { 6 | function printObject (data, header){ 7 | var printTop = false 8 | if (header === undefined) { 9 | printTop = false 10 | header = "**********************" 11 | } else { 12 | header = " " + header + " " 13 | } 14 | 15 | var filler = "" 16 | for (var i = header.length + 6; i > 0; --i) { 17 | filler += "*" 18 | } 19 | 20 | if (printTop) { 21 | console.log("/" + filler + "/") 22 | } 23 | 24 | console.log("/***" + header + "***/") 25 | _p.printObjectInternal(data) 26 | console.log("/" + filler + "/") 27 | } 28 | 29 | //-------------------------------------------------------------------------- 30 | Item { 31 | id: _p 32 | 33 | function printObjectInternal(data, prefix, level) 34 | { 35 | if (typeof prefix === 'undefined' || typeof prefix === 'null') { 36 | prefix = " "; 37 | } 38 | if (typeof level === 'undefined' || typeof prefix === 'null') { 39 | level = 0; 40 | } else if (level > 5) 41 | return; 42 | 43 | var indentStr = "" 44 | for (var i = 0; i < level; ++i) { 45 | indentStr += prefix; 46 | } 47 | 48 | if (Object.prototype.toString.call(data) == '[object Object]') { 49 | var funcs = [] 50 | for (var key in data) { 51 | if (Object.prototype.toString.call(data[key]) == '[object Object]') { 52 | if (key == 'parent' || key == 'objectName' || data[key] === data) { 53 | continue; 54 | } 55 | 56 | console.log(indentStr + key + " " + data[key]) 57 | printObjectInternal(data[key], prefix , level + 1); 58 | } else if (Object.prototype.toString.call(data[key]) == '[object Function]') { 59 | if (key.slice(0,2) !== "__") { 60 | funcs.push(key) 61 | } 62 | continue; 63 | } else if (Object.prototype.toString.call(data[key]) == '[object Array]') { 64 | console.log(indentStr + key + " [Array]"); 65 | printObjectInternal(data[key], prefix, level + 1) 66 | } else { 67 | console.log (indentStr + key + " = " + data[key] + " [" + typeof(data[key]) + "]"); 68 | } 69 | } 70 | 71 | if (funcs.length !== 0) { 72 | console.log(indentStr + "Functions:") 73 | for (key in funcs) { 74 | console.log(indentStr + prefix + funcs[key]) 75 | } 76 | } 77 | } else if (Object.prototype.toString.call(data) == '[object Array]') { 78 | for (i = 0; i < data.length; ++i) { 79 | if (data[i] == undefined) { 80 | continue; 81 | } 82 | 83 | console.log(indentStr + "[" + i + "]") 84 | printObjectInternal(data[i], prefix, level + 1) 85 | } 86 | } else { 87 | var str = ""; 88 | str += indentStr + Object.prototype.toString.call(data) + " " + data; 89 | console.log (indentStr + data + " [" + typeof(data) + "]"); 90 | } 91 | } 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /qml/DebugLayout.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | //-------------------------------------------------------------------------- 4 | // Крест, показывающий центр родителя 5 | //-------------------------------------------------------------------------- 6 | Item { 7 | id: _debugLayout 8 | 9 | property color color: "red" 10 | anchors.fill: parent 11 | z: 1000 12 | 13 | //-------------------------------------------------------------------------- 14 | Rectangle { 15 | width: 1 16 | height: parent.height 17 | color: _debugLayout.color 18 | anchors.centerIn: parent 19 | } 20 | 21 | //-------------------------------------------------------------------------- 22 | Rectangle { 23 | width: parent.width 24 | height: 1 25 | color: _debugLayout.color 26 | anchors.centerIn: parent 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /qml/DebugRectangle.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "./" 4 | import "../" 5 | 6 | //------------------------------------------------------------------------------ 7 | // Прямоугольник для отладки размеров и положения элементов 8 | //------------------------------------------------------------------------------ 9 | Rectangle { 10 | id: _debugRectangle 11 | 12 | anchors.fill: parent 13 | color: "green" 14 | opacity: 0.5 15 | z: 1000 16 | } 17 | -------------------------------------------------------------------------------- /qml/DebugTimer.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.5 2 | 3 | //------------------------------------------------------------------------------ 4 | // Таймер для отладки изменения какого-нибудь поля 5 | //------------------------------------------------------------------------------ 6 | Timer { 7 | id: debugTimer 8 | 9 | running: true 10 | repeat : true 11 | interval: 500 12 | } 13 | -------------------------------------------------------------------------------- /qml/DrawerPrototype.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "qrc:/SpQml" 4 | import "./" 5 | import "../" 6 | 7 | Rectangle { 8 | id: drawer 9 | 10 | readonly property alias shown: _p.shown; 11 | 12 | width: Consts.drawerWidth 13 | height: parent.height 14 | 15 | Behavior on x { 16 | SmoothedAnimation { velocity: width/0.3} 17 | } 18 | 19 | MouseArea { 20 | id: stubMa 21 | 22 | anchors.fill: parent 23 | onClicked: {} 24 | } 25 | 26 | //-------------------------------------------------------------------------- 27 | MouseArea { 28 | id: maForShowAndHide 29 | 30 | anchors { 31 | top: parent.top 32 | left: parent.right 33 | bottom: parent.bottom 34 | } 35 | 36 | drag.target: drawer 37 | drag.axis: Drag.XAxis 38 | drag.minimumX: -drawer.width 39 | drag.maximumX: 0 40 | 41 | onReleased: { 42 | if (shown) { 43 | if (drawer.x < -drawer.width/3) { 44 | _p.shown = false; 45 | } else { 46 | drawer.x = 0 47 | } 48 | } else { 49 | if (drawer.x > -drawer.width*2/3) { 50 | _p.shown = true; 51 | } else { 52 | drawer.x = -drawer.width 53 | } 54 | } 55 | } 56 | 57 | onClicked: { 58 | if (shown) { 59 | hide(); 60 | } 61 | } 62 | } 63 | 64 | //-------------------------------------------------------------------------- 65 | Rectangle { 66 | id: fade 67 | 68 | opacity: _p.shift 69 | color: "#4d000000" 70 | anchors { 71 | top: parent.top 72 | left: parent.right 73 | bottom: parent.bottom 74 | } 75 | width: Consts.maxWidth 76 | } 77 | 78 | //-------------------------------------------------------------------------- 79 | Rectangle { 80 | id: shadow 81 | 82 | visible: drawer.x !== -drawer.width 83 | width: parent.height 84 | height: 1*mm 85 | rotation: -90 86 | anchors { 87 | left: parent.right 88 | top: parent.bottom 89 | } 90 | transformOrigin: Item.TopLeft 91 | gradient: Gradient { 92 | GradientStop { position: 0.0; color: "#30000000"} 93 | GradientStop { position: 0.2; color: "#15000000"} 94 | GradientStop { position: 1.0; color: "#00000000"} 95 | } 96 | } 97 | 98 | //-------------------------------------------------------------------------- 99 | Item { 100 | id: _p 101 | 102 | property bool shown: false; 103 | property double shift: 1 + drawer.x/drawer.width 104 | } 105 | 106 | //-------------------------------------------------------------------------- 107 | Item { 108 | id: statesItem 109 | 110 | state: "hidden" 111 | states: [ 112 | State { 113 | name: "hidden" 114 | when: !drawer.shown 115 | 116 | PropertyChanges { 117 | target: drawer 118 | x: -width 119 | } 120 | PropertyChanges { 121 | target: maForShowAndHide 122 | width: Consts.drawerDragWidth 123 | } 124 | }, 125 | State { 126 | name: "shown" 127 | when: drawer.shown 128 | 129 | PropertyChanges { 130 | target: drawer 131 | x: 0 132 | } 133 | PropertyChanges { 134 | target: shadow 135 | visible: true 136 | } 137 | PropertyChanges { 138 | target: maForShowAndHide 139 | width: Consts.maxWidth 140 | } 141 | } 142 | ] 143 | } 144 | 145 | //-------------------------------------------------------------------------- 146 | function show() { 147 | _p.shown = true; 148 | } 149 | 150 | //-------------------------------------------------------------------------- 151 | function hide() { 152 | _p.shown = false; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /qml/FullScreenImage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import SP 1.0 3 | 4 | import "./" 5 | import "../" 6 | 7 | Item { 8 | id: _fullScreenPhoto 9 | 10 | property string photoSource: "" 11 | property alias downloadButtonVisible: downloadButton.visible 12 | readonly property bool isReady: scaleblePhoto.item !== null && scaleblePhoto.item.isReady 13 | 14 | signal clicked() 15 | 16 | //-------------------------------------------------------------------------- 17 | Rectangle { 18 | anchors.fill: parent 19 | color: "black" 20 | } 21 | 22 | //-------------------------------------------------------------------------- 23 | Zoom { 24 | id: scaleblePhoto 25 | 26 | sourceComponent: Item { 27 | property alias sidesRatio: _onlineImage.sidesRatio 28 | property alias isReady : _onlineImage.isReady 29 | 30 | ImageSp { 31 | id: _onlineImage 32 | 33 | readonly property bool isReady: status === ImageSp.Ready 34 | readonly property real sidesRatio: sourceSize.height !== 0 35 | ? sourceSize.width / sourceSize.height 36 | : 1 37 | 38 | fillMode: Image.PreserveAspectFit 39 | source: photoSource 40 | scale: isReady 41 | ? parent.width/width < parent.height/height 42 | ? parent.width/width 43 | : parent.height/height 44 | : 1 45 | anchors.centerIn: parent 46 | } 47 | } 48 | 49 | onClicked: { 50 | _fullScreenPhoto.clicked() 51 | } 52 | 53 | width: _fullScreenPhoto.width 54 | height: _fullScreenPhoto.height 55 | }//Zoom { id: scaleblePhoto 56 | 57 | //-------------------------------------------------------------------------- 58 | // Кнопка сохранить 59 | //-------------------------------------------------------------------------- 60 | Image { 61 | id: downloadButton 62 | 63 | anchors { 64 | top : parent.top 65 | right: parent.right 66 | rightMargin: Consts.margin 67 | topMargin: 1.2*Consts.margin + Consts.statusBarHeight 68 | } 69 | height: 2*Consts.margin 70 | width: height 71 | enabled: visible 72 | 73 | // Так делаь плохо. Подумать, что сделать с изображениями в sp_qt_libs 74 | source: "qrc:/download.png" 75 | opacity: scaleblePhoto.item !== null && scaleblePhoto.item.isReady 76 | ? 1.0 77 | : 0.4 78 | 79 | MouseArea { 80 | anchors.fill: parent 81 | anchors.margins: -Consts.margin 82 | enabled: parent.visible 83 | onClicked: { 84 | downloadButton.saveImage() 85 | } 86 | } 87 | 88 | function saveImage() { 89 | if (!imageSaverSp.checkFile(_fullScreenPhoto.photoSource)) { 90 | var resultPath = imageSaverSp.save(_fullScreenPhoto.photoSource); 91 | 92 | if (resultPath !== "") { 93 | toast.showMessage( qsTr("Фото сохранено")); 94 | } else { 95 | toast.showMessage( qsTr("Ошибка! Недостаточно памяти для сохранения фото.")); 96 | } 97 | } 98 | } 99 | } // Image { id: downloadButton 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /qml/FullScreenImageView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "./" 4 | import "../" 5 | 6 | ListView { 7 | id: _fullScreenImageView 8 | 9 | property KeysHandler keysHandler 10 | property var imagesList: [] 11 | 12 | signal hide() 13 | signal show() 14 | 15 | width: Window.width 16 | height: Window.height 17 | model: imagesList.length 18 | interactive: visible 19 | enabled: visible 20 | visible: false 21 | snapMode: ListView.SnapOneItem 22 | highlightRangeMode: ListView.StrictlyEnforceRange 23 | orientation: ListView.Horizontal 24 | boundsBehavior: ListView.StopAtBounds 25 | cacheBuffer: 2*width 26 | 27 | delegate: FullScreenImage { 28 | width: Window.width 29 | height: Window.height 30 | photoSource: imagesList[index] 31 | visible: true 32 | downloadButtonVisible: false 33 | 34 | onClicked: { 35 | if (actionBarStateItem.state === "visible") { 36 | actionBarStateItem.state = "invisible" 37 | } else { 38 | actionBarStateItem.state = "visible" 39 | } 40 | 41 | } 42 | } 43 | 44 | Rectangle { 45 | width: parent.width 46 | height: Consts.statusBarHeight 47 | color: actionBar.color 48 | } 49 | 50 | //-------------------------------------------------------------------------- 51 | ActionBar { 52 | id: actionBar 53 | 54 | title: _fullScreenImageView.imagesList.length > 1 55 | ? qsTr("%1 из %2") 56 | .arg(_fullScreenImageView.currentIndex+1) 57 | .arg(_fullScreenImageView.imagesList.length) 58 | : "" 59 | 60 | titleColor: "white" 61 | color: Qt.rgba(0, 0, 0, 0.4); 62 | titleAlignment: Text.AlignHCenter 63 | width: Window.width 64 | shadowVisible: false 65 | y: Consts.statusBarHeight 66 | 67 | leftButton.sourceComponent: BackButton { 68 | id: backButton 69 | arrowColor: "white" 70 | y: Math.ceil((Consts.actionBarHeight - height)/2) 71 | z: actionBar.z 72 | 73 | Connections { 74 | target: keysHandler 75 | onBackKeyPressed: { 76 | if (!context.accepted && backButton.enabled && backButton.visible) { 77 | context.accepted = true; 78 | _fullScreenImageView.backAction(); 79 | } 80 | } 81 | } 82 | 83 | onClicked: { 84 | _fullScreenImageView.backAction() 85 | } 86 | } // BackButton { 87 | 88 | rightButton.sourceComponent: MaterialButton { 89 | id: downloadButton 90 | 91 | color: "transparent" 92 | width: height-2*Consts.spacing 93 | radius: height/2 94 | border.width: 0 95 | pressedColor: "black" 96 | 97 | y: Math.ceil((Consts.actionBarHeight - height)/2) 98 | 99 | enabled: visible 100 | opacity: _fullScreenImageView.currentItem && _fullScreenImageView.currentItem.isReady 101 | ? 1.0 102 | : 0.4 103 | 104 | //-------------------------------------------------------------------------- 105 | Image { 106 | source: "qrc:/download.png" 107 | 108 | readonly property int originalHeight: isDesktop 109 | ? Math.ceil(2.7*mm) 110 | : Math.ceil(2.5*mm) 111 | height: originalHeight % 2 === 0 112 | ? originalHeight 113 | : originalHeight + 1 114 | width: height 115 | anchors.centerIn: parent 116 | fillMode: Image.PreserveAspectFit 117 | } // Image { 118 | 119 | onClicked: { 120 | _fullScreenImageView.saveImage() 121 | } 122 | } // MaterialButton { 123 | } // ActionBar { 124 | 125 | //-------------------------------------------------------------------------- 126 | Item { 127 | id: actionBarStateItem 128 | 129 | state: "visible" 130 | states: [ 131 | State { 132 | name: "visible" 133 | PropertyChanges { 134 | target: actionBar 135 | opacity: 1.0 136 | } 137 | } 138 | , State { 139 | name: "invisible" 140 | PropertyChanges { 141 | target: actionBar 142 | opacity: 0.0 143 | } 144 | } 145 | ] 146 | 147 | transitions: [ 148 | Transition { 149 | from: "visible" 150 | to: "invisible" 151 | reversible: true 152 | 153 | NumberAnimation { 154 | target: actionBar 155 | property: "opacity" 156 | easing.type: Easing.OutQuart 157 | duration: 250 158 | } 159 | } 160 | ,Transition { 161 | to: "visible" 162 | from: "invisible" 163 | 164 | NumberAnimation { 165 | target: actionBar 166 | property: "opacity" 167 | easing.type: Easing.OutQuart 168 | duration: 150 169 | } 170 | } 171 | ] 172 | } // Item { id: actionBarStateItem 173 | 174 | //-------------------------------------------------------------------------- 175 | function saveImage() { 176 | if (!imageSaverSp.checkFile(imagesList[_fullScreenImageView.currentIndex], Config.applicationCaption)) { 177 | var resultPath = imageSaverSp.save(imagesList[_fullScreenImageView.currentIndex], Config.applicationCaption); 178 | if (resultPath !== "") { 179 | toast.showMessage( qsTr("Фото сохранено в папке %1.").arg(Config.applicationCaption)); 180 | } else { 181 | toast.showMessage( qsTr("Ошибка! Недостаточно памяти для сохранения фото.")); 182 | } 183 | } else { 184 | toast.showMessage( qsTr("Фото уже сохранено в папке %1.").arg(Config.applicationCaption)); 185 | } 186 | } // function saveImage() { 187 | 188 | //-------------------------------------------------------------------------- 189 | function backAction() { 190 | hideFullscreen() 191 | } // function backAction() { 192 | 193 | //-------------------------------------------------------------------------- 194 | function showFullscreen(imagesArr, currentIndex) { 195 | if (currentIndex === undefined || currentIndex < 0) { 196 | currentIndex = 0 197 | } 198 | 199 | _fullScreenImageView.imagesList = imagesArr 200 | _fullScreenImageView.positionViewAtIndex(currentIndex, ListView.Center) 201 | _fullScreenImageView.visible = true 202 | show() 203 | } 204 | 205 | //-------------------------------------------------------------------------- 206 | function hideFullscreen() { 207 | _fullScreenImageView.visible = false 208 | hide() 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /qml/KeysHandler.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | //------------------------------------------------------------------------------ 4 | // Обработчик нажатий на кнопки (в том числе кнопку "Назад" в Android). 5 | //------------------------------------------------------------------------------ 6 | Item { 7 | id: _keysHandler 8 | 9 | signal backKeyPressed (var context) 10 | signal keyPressed (var context,var event) 11 | 12 | focus: true 13 | 14 | Keys.onReleased: { 15 | event.accepted = true 16 | var context = { 17 | accepted: false 18 | , key: event.key 19 | }; 20 | 21 | if (event.key === Qt.Key_Back || event.key === Qt.Key_Escape) { 22 | backKeyPressed(context); 23 | 24 | if (!context.accepted) { 25 | if (_p.isExit) { 26 | Qt.quit() 27 | } else { 28 | _p.isExit = true 29 | exit_timer.start() 30 | toast.showMessage(qsTr("Для выхода из приложения нажмите \"Назад\" еще раз.")) 31 | } 32 | } 33 | } else { 34 | keyPressed (context, event); 35 | } 36 | 37 | event.accepted = true 38 | }// Keys.onReleased: { 39 | 40 | //------------------------------------------------------------------------------ 41 | Timer { 42 | id: exit_timer 43 | interval: 1500 44 | running: !_p.isExit 45 | onTriggered: { 46 | _p.isExit = false; 47 | } 48 | } // Timer { 49 | 50 | //------------------------------------------------------------------------------ 51 | QtObject { 52 | id: _p 53 | 54 | property bool isExit: false 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /qml/MaterialButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "./" 4 | import "../" 5 | 6 | //------------------------------------------------------------------------------ 7 | // @brief Прототип кнопки с материальным дизайном (анимация фона при нажатии) 8 | //------------------------------------------------------------------------------ 9 | ButtonPrototype { 10 | id: _materialButton 11 | 12 | property alias text: textItem.text 13 | property alias font: textItem.font 14 | property alias textItem: textItem 15 | property color textDisabledColor: "#88000000" 16 | property alias color: background.color 17 | property alias pressedColor: pressedPlace.color 18 | property color disabledColor: color 19 | property alias radius: background.radius 20 | property alias border: background.border 21 | property alias pressedPlaceOpacity: pressedPlace.opacity 22 | 23 | property int durationAnimationPressed: 400 24 | property int durationAnimationReleased: 200 25 | 26 | width: textItem.width + 2*Consts.margin 27 | 28 | Rectangle { 29 | id: background 30 | 31 | color: Consts.buttonActiveColor 32 | anchors.fill: parent 33 | clip: true 34 | radius: Consts.radius 35 | 36 | //-------------------------------------------------------------------------- 37 | // TODO При нажатии сделать, чтобы углы оставались закгруглёнными 38 | //-------------------------------------------------------------------------- 39 | Rectangle { 40 | id: pressedPlace 41 | 42 | readonly property double rightShift: pressedX+width/2 - background.width 43 | readonly property double leftShift: width/2 - pressedX 44 | readonly property bool inEdge: rightShift > 0 || leftShift > 0 45 | 46 | x: rightShift > 0 47 | ? pressedX - rightShift 48 | : leftShift > 0 49 | ? pressedX + leftShift 50 | : pressedX 51 | y: parent.height/2 52 | height: Math.min(width, background.height) 53 | color: "gray" 54 | opacity: 0.1 55 | border { 56 | width: background.border.width 57 | color: background.border.color 58 | } 59 | radius: inEdge 60 | ? background.radius 61 | : height/2 62 | transform: Translate { 63 | x: -pressedPlace.width/2 64 | y: -pressedPlace.height/2 65 | } 66 | } 67 | } // Rectangle { id: background 68 | 69 | //-------------------------------------------------------------------------- 70 | Text { 71 | id: textItem 72 | 73 | anchors.centerIn: parent 74 | font.pixelSize: Consts.fontNormal 75 | } 76 | 77 | //-------------------------------------------------------------------------- 78 | Item { 79 | id: statesItem 80 | 81 | states: [ 82 | State { 83 | name: "disabled" 84 | when: !_materialButton.enabled 85 | PropertyChanges { 86 | target: textItem 87 | color: textDisabledColor 88 | } 89 | PropertyChanges { 90 | target: _materialButton 91 | color: disabledColor 92 | } 93 | } 94 | ,State { 95 | name: "pressed" 96 | when: isPressed 97 | PropertyChanges { 98 | target: pressedPlace 99 | //radius: materialButton.width/2 100 | width: background.width 101 | } 102 | } 103 | ] 104 | 105 | transitions: [ 106 | Transition { 107 | to: "pressed" 108 | SequentialAnimation { 109 | NumberAnimation { 110 | target: pressedPlace 111 | property: "width" 112 | easing.type: Easing.InQuad 113 | duration: durationAnimationPressed 114 | } 115 | } 116 | } 117 | ,Transition { 118 | from: "pressed" 119 | NumberAnimation { 120 | target: pressedPlace 121 | property: "width" 122 | duration: durationAnimationReleased 123 | } 124 | } 125 | ] 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /qml/MenuButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "./" 4 | import "../" 5 | 6 | //------------------------------------------------------------------------------ 7 | // @brief Кнопка открытия меню с иконкой в виде бутерброда. 8 | //------------------------------------------------------------------------------ 9 | MaterialButton { 10 | id: menuButton 11 | 12 | //property string color: "white" 13 | 14 | color: "transparent" 15 | width: height 16 | 17 | 18 | Column { 19 | anchors.centerIn: parent 20 | spacing: Math.ceil(0.5*mm) 21 | 22 | Rectangle { 23 | id: rectangleTop 24 | 25 | color: menuButton.color 26 | height: Consts.lineWidth 27 | width: 4*mm 28 | } 29 | 30 | Rectangle { 31 | id: rectangleMiddle 32 | 33 | color: menuButton.color 34 | height: Consts.lineWidth 35 | width: 4*mm 36 | } 37 | 38 | Rectangle { 39 | id: rectangleBottom 40 | 41 | color: menuButton.color 42 | height: Consts.lineWidth 43 | width: 4*mm 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /qml/ShadowBottom.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "./" 4 | import "../" 5 | 6 | Rectangle { 7 | id: shadowBottom 8 | 9 | width: parent.width 10 | height: 1*mm 11 | gradient: Gradient { 12 | GradientStop { position: 0.0; color: "#30000000"} 13 | GradientStop { position: 0.2; color: "#15000000"} 14 | GradientStop { position: 1.0; color: "#00000000"} 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /qml/ShadowRectangle.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import ".." 4 | 5 | Item { 6 | id: _shadowRectangle 7 | 8 | property double step: size/countOfShards 9 | property double size: Consts.margin 10 | property double radius: Consts.radius 11 | property color color: Qt.rgba(0.5, 0.5, 0.5, 0.04) 12 | property int countOfShards: 8 // Количество прямоугольников для отрисовки тени 13 | 14 | Repeater { 15 | model: countOfShards 16 | 17 | Rectangle { 18 | id: _shadowShard 19 | 20 | readonly property double increment: index*step 21 | 22 | color: _shadowRectangle.color 23 | radius: _shadowRectangle.radius + index*index*step/10; 24 | anchors { 25 | fill: parent 26 | margins: -_shadowShard.increment 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /qml/ShadowTop.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | import "./" 4 | import "../" 5 | 6 | Rectangle { 7 | id: shadowTop 8 | 9 | width: parent.width 10 | height: 1*mm 11 | gradient: Gradient { 12 | GradientStop { position: 0.0; color: "#00000000"} 13 | GradientStop { position: 0.8; color: "#15000000"} 14 | GradientStop { position: 1.0; color: "#30000000"} 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /qml/SmileForRate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import SP 1.0 3 | 4 | // Оценка с помощью смайла. Для работы требуются Arc & ArcFast поключаемые с помощью 5 | 6 | Item { 7 | id: smileForRate 8 | 9 | property var evaluations: ["Ужасно","Плохо","Нормально","Хорошо","Отлично"] 10 | property alias backgroundColor: background.color 11 | property bool useFastArc: false 12 | property double penWidth: mm 13 | property double currentRate: Math.round(evaluations.length * 10*(_p.dragTargetDelta + dragTarget.y) / (2*_p.dragTargetDelta)) / 10 14 | 15 | //-------------------------------------------------------------------------- 16 | Rectangle { 17 | id: background 18 | color: "transparent" 19 | anchors.fill: parent 20 | } 21 | 22 | Text { 23 | id: counterItem 24 | 25 | text: currentRate+".0/" + evaluations.length + ".0" 26 | color: "black" 27 | font.pixelSize: 4*mm 28 | 29 | anchors { 30 | top: parent.top 31 | topMargin: margin 32 | horizontalCenter: parent.horizontalCenter 33 | } 34 | } 35 | 36 | Text { 37 | text: evaluations[Math.min(Math.floor(currentRate), evaluations.length - 1)] 38 | color: "black" 39 | font.pixelSize: 2.5*mm 40 | 41 | anchors { 42 | top: counterItem.bottom 43 | topMargin: margin 44 | horizontalCenter: parent.horizontalCenter 45 | } 46 | } 47 | 48 | //-------------------------------------------------------------------------- 49 | Rectangle { 50 | id: smileCircle 51 | 52 | anchors.horizontalCenter: parent.horizontalCenter 53 | anchors.centerIn: parent 54 | width: parent.width - 2*margin - 2*penWidth 55 | height: width 56 | radius: width / 2 57 | color: "transparent" 58 | border { 59 | width: penWidth 60 | color: _p.arcColor 61 | } 62 | 63 | //-------------------------------------------------------------------------- 64 | Loader { 65 | id: arcLoader 66 | 67 | sourceComponent: smileForRate.useFastArc 68 | ? arcFastComponent 69 | : arcComponent 70 | 71 | width: 0.5*parent.width 72 | height: width 73 | 74 | anchors.centerIn: parent 75 | anchors.verticalCenterOffset: 0.2*parent.height 76 | } 77 | 78 | } 79 | 80 | 81 | //-------------------------------------------------------------------------- 82 | Item { 83 | id: dragTarget 84 | 85 | height: parent.height 86 | width: parent.width 87 | } 88 | 89 | //-------------------------------------------------------------------------- 90 | MouseArea { 91 | anchors.fill: parent 92 | drag.target: dragTarget 93 | drag.axis: Drag.YAxis 94 | drag.minimumY: -_p.dragTargetDelta 95 | drag.maximumY: _p.dragTargetDelta 96 | } 97 | 98 | //-------------------------------------------------------------------------- 99 | // Дуга с отрисовкой в QPainter 100 | //-------------------------------------------------------------------------- 101 | Component { 102 | id: arcComponent 103 | 104 | Arc { 105 | anchors.fill: parent 106 | startAngle: _p.startArcAngle 107 | spanAngle: 150 108 | penWidth: smileForRate.penWidth 109 | arcHeight: _p.arcHeight 110 | color: _p.arcColor 111 | } 112 | 113 | } 114 | 115 | //-------------------------------------------------------------------------- 116 | // Дуга с отрисовкой в SceneGraph 117 | //-------------------------------------------------------------------------- 118 | Component { 119 | id: arcFastComponent 120 | 121 | ArcFast { 122 | spanAngle: 0 123 | penWidth: 0.25*mm 124 | } 125 | } 126 | 127 | //-------------------------------------------------------------------------- 128 | QtObject { 129 | id: _p 130 | 131 | property double dragTargetDelta: 0.7*arcLoader.height 132 | property double startArcAngle: dragTarget.y < 0 133 | ? 15 134 | : 195 135 | 136 | property double arcHeight: dragTarget.y < 0 137 | ? -dragTarget.y 138 | : dragTarget.y 139 | 140 | property double delta: arcHeight/_p.dragTargetDelta 141 | 142 | property color arcColor: dragTarget.y < 0 143 | ? Qt.rgba( // От синего до красного 144 | Math.max(122/255 + 58/255*delta*4, 180/255) 145 | , Math.max(159/255 - 48/255*delta*4, 111/255) 146 | , Math.max(197/255 - 87/255*delta*4, 110/255) 147 | , 1 ) 148 | : dragTarget.y === 0 149 | ? Qt.rgba(122,159,197,1) 150 | : Qt.rgba( // От синего до зеленого 151 | Math.max(122/255 - 7/255*delta *2, 115/255) 152 | , Math.max(159/255 + 35/255*delta*2, 194/255) 153 | , Math.max(197/255 - 27/255*delta*2, 170/255) 154 | , 1 ) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /qml/StackView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import "../" 3 | import "./" 4 | 5 | // Контейнер, подгружающий компоненты один за другим. 6 | Item { 7 | id: stackView 8 | 9 | // Массив компонентов для отображения. 10 | property var components 11 | 12 | // Индекс начального компонента для загрузки 13 | property int initialComponent: 0 14 | 15 | // Количество компонентов в массиве. 16 | readonly property int count: components.length 17 | 18 | // Текущий индекс отображаемого компонента. 19 | readonly property alias currentIndex: _p.currentIndex 20 | 21 | // Индекс элемента, который станет следующим. Выставляется перед сменой компонентов. 22 | readonly property alias nextCurrentIndex: _p.nextCurrentIndex 23 | 24 | // Сигнал о началае смены компонентов. 25 | // \param currentIndex - индекс текущего элемента, до смены компонентов 26 | // \param nextCurrentIndex - индекс следующиего текущего элемента, после смены 27 | signal startSwitch (var currentIndex, var nextCurrentIndex) 28 | 29 | // Сигнал об окончании смены компонентов. 30 | // \param currentIndex - индекс текущего элемента, после сменыт компонентов 31 | // \param previousCurrentIndex - индекс предыдущего текущего элемента 32 | signal switched (var currentIndex, var previousCurrentIndex) 33 | 34 | //-------------------------------------------------------------------------- 35 | Loader { 36 | id: firstLoader 37 | 38 | property int index: 0 39 | 40 | sourceComponent: stackView.components[initialComponent] 41 | anchors.fill: parent 42 | } 43 | 44 | Loader { 45 | id: secondLoader 46 | 47 | property int index 48 | 49 | anchors.fill: parent 50 | } 51 | 52 | //-------------------------------------------------------------------------- 53 | Item { 54 | id: statesItem 55 | 56 | states: [ 57 | State { 58 | name: "firstLoader" 59 | PropertyChanges { 60 | target: firstLoader 61 | visible: true 62 | } 63 | PropertyChanges { 64 | target: secondLoader 65 | visible: false 66 | } 67 | } 68 | ,State { 69 | name: "secondLoader" 70 | PropertyChanges { 71 | target: firstLoader 72 | visible: false 73 | } 74 | PropertyChanges { 75 | target: secondLoader 76 | visible: true 77 | } 78 | } 79 | ] 80 | } 81 | 82 | //-------------------------------------------------------------------------- 83 | Item { 84 | id: _p 85 | 86 | property int currentIndex: 0 87 | property int nextCurrentIndex: 0 88 | } 89 | 90 | //-------------------------------------------------------------------------- 91 | // Меняет текущий отображемый компонент с следующий в списке 92 | function next() { 93 | if (_p.currentIndex+1 >= count) { 94 | return 95 | } 96 | 97 | var previousCurrentIndex = _p.currentIndex; 98 | 99 | ++_p.nextCurrentIndex; 100 | 101 | startSwitch (_p.currentIndex, _p.nextCurrentIndex) 102 | 103 | if (statesItem.state === "firstLoader") { 104 | secondLoader.index = nextCurrentIndex; 105 | secondLoader.sourceComponent = components[_p.nextCurrentIndex]; 106 | statesItem.state = "secondLoader"; 107 | firstLoader.sourceComponent = null; 108 | } else { 109 | firstLoader.index = nextCurrentIndex; 110 | firstLoader.sourceComponent = components[_p.nextCurrentIndex]; 111 | statesItem.state = "fistLoader"; 112 | secondLoader.sourceComponent = null; 113 | } 114 | 115 | _p.currentIndex = _p.nextCurrentIndex; 116 | switched (_p.currentIndex, previousCurrentIndex); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /qml/SwipeAnimationDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | 3 | // 4 | // Компонент горизонтального листания Swip'ом 5 | // 6 | // Инициализируется подобно ListView, за тем исключением, что в делегате 7 | // нужно прописать свойство: 8 | // property QtObject model 9 | // И обращаться к данным модели только через него. 10 | // 11 | // Перелистывание элементов к следующему, предыдущему производится функциями 12 | // incrementCurrentIndex() и decrementCurrentIndex(). 13 | // Для пролистывания к конкретном элементу - функция 14 | // setCurrentIndex (index, withAnimation) 15 | // 16 | //Item { 17 | Rectangle { 18 | id: swipeAnimationDelegate 19 | 20 | transform: Translate { x: shift } 21 | 22 | property double shift 23 | property var loadedObject 24 | 25 | // Заглушка, препятсвующая нажатия внутри событий. 26 | MouseArea { 27 | anchors.fill: parent 28 | onClicked: { } 29 | } 30 | 31 | // Затемнение соседних элементов 32 | Rectangle { 33 | id: fade 34 | color: "black" 35 | opacity: .0 36 | anchors.fill: parent 37 | z: 40 38 | scale: 1/parent.scale 39 | } 40 | 41 | // Тень справа 42 | Rectangle { 43 | id: shadow 44 | height: consts.ui_margin 45 | rotation: -90 46 | visible: false 47 | transformOrigin: Item.TopLeft 48 | width: swipeAnimationDelegate.height 49 | anchors { 50 | left: parent.right 51 | top: parent.bottom 52 | } 53 | 54 | gradient: Gradient { 55 | GradientStop { position: 0.0; color: "#30000000"} 56 | GradientStop { position: 0.2; color: "#15000000"} 57 | GradientStop { position: 1.0; color: "#00000000"} 58 | } 59 | } 60 | 61 | states: [ 62 | State { 63 | name: "active" 64 | 65 | PropertyChanges { 66 | target: shadow 67 | visible: true 68 | height: consts.ui_margin*(1 - listView.shiftXByWidth) 69 | } 70 | } 71 | ,State { 72 | name: "right" 73 | 74 | PropertyChanges { 75 | target: swipeAnimationDelegate 76 | z : -1 77 | shift: -width + listView.shiftX 78 | scale: 0.9 + 0.1*listView.shiftXByWidth 79 | opacity: 0.1 + 0.9*Math.pow(listView.shiftXByWidth,3) 80 | } 81 | PropertyChanges { 82 | target: fade 83 | opacity: 0.2*(1-listView.shiftXByWidth) 84 | } 85 | } 86 | ] 87 | 88 | property double diffIndex: index - listView.currentIndex; 89 | onDiffIndexChanged: { 90 | setState(); 91 | } 92 | 93 | function setState () { 94 | switch (diffIndex) { 95 | case -1: state = ""; break; 96 | case +1: state = "right"; break; 97 | case 0: state = "active"; break; 98 | default: state = ""; break; 99 | } 100 | } 101 | 102 | Component.onCompleted: { 103 | setState(); 104 | } 105 | 106 | Component.onDestruction: { 107 | //QmlHelper.destroyQMLObject (loadedObject); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /qml/SwipeView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Window 2.2 3 | 4 | // 5 | // Компонент горизонтального листания Swip'ом 6 | // 7 | // Инициализируется подобно ListView, за тем исключением, что в делегате 8 | // нужно прописать свойство: 9 | // property QtObject model 10 | // И обращаться к данным модели только через него. 11 | // 12 | // Перелистывание элементов к следующему, предыдущему производится функциями 13 | // incrementCurrentIndex() и decrementCurrentIndex(). 14 | // Для пролистывания к конкретном элементу - функция 15 | // setCurrentIndex (index, withAnimation) 16 | // 17 | Item { 18 | id: swipeView 19 | 20 | property alias model : listView.model 21 | property alias delegate: listView.delegate 22 | 23 | property double initialIndex: 0 // Начальный индекс с которого начинается просмотр 24 | readonly property alias activeIndex: listView._activeIndex // Активный индекс, т.е. на котором остановился SwipeView 25 | readonly property alias currentIndex: listView._currentIndex // Текущий индекс элемента. @note Может несколько раз поменяться после setCurrentIndex 26 | readonly property alias currentItem: listView.currentItem // Текущий элемент в списке 27 | readonly property alias isMoving: listView.isMoving // Флаг того, что листание в процессе. 28 | property double maximumFlickVelocity: 3 * width // Скорость анимации пролистывания 29 | property alias animationVelocity: swipeView.maximumFlickVelocity 30 | property double maxAnimationDuration: 400 // Максимальная длительность анимации пролистывания 31 | property bool interactive: true // Флаг интерактивность swipeView 32 | property bool withAnimation: false 33 | readonly property alias count: listView.count 34 | readonly property alias movedBySwipe: listView.movedBySwipe // Флаг того, что currentIndex поменялся в результате изменения swip'ом 35 | readonly property alias shiftX: listView.shiftX 36 | readonly property alias shiftXByWidth: listView.shiftXByWidth 37 | //TODO Вынести в параметры ориентацию списка (сейчас только горизонтальная) 38 | 39 | //-------------------------------------------------------------------------- 40 | ListView { 41 | id: listView 42 | 43 | property bool isMoving: false 44 | property bool movedBySwipe: false 45 | property int _currentIndex: Math.floor((diffX + width/2)/ width) 46 | property int _activeIndex: 0 47 | readonly property double inEdge: diffX % width === 0 48 | readonly property double diffX: contentX - originX 49 | readonly property double shiftX: diffX - (_currentIndex)*width 50 | readonly property double shiftXByWidth: shiftX / width 51 | 52 | signal needSetState 53 | 54 | orientation: ListView.Horizontal 55 | snapMode: ListView.SnapOneItem 56 | maximumFlickVelocity: swipeView.maximumFlickVelocity 57 | anchors.fill: parent 58 | visible: initialIndex !== 0 59 | boundsBehavior: Flickable.StopAtBounds 60 | interactive: swipeView.interactive //&& !flicking 61 | cacheBuffer: 2*width 62 | 63 | onDragStarted: { 64 | movedBySwipe = true; 65 | } 66 | 67 | onInEdgeChanged: { 68 | if (inEdge) { 69 | interactive = true && swipeView.interactive; 70 | } 71 | isMoving = !inEdge; 72 | } 73 | 74 | onIsMovingChanged: { 75 | if (!isMoving) { 76 | _activeIndex = _currentIndex; 77 | } 78 | } 79 | 80 | Behavior on contentX { 81 | id: behOnContentX 82 | enabled: false 83 | 84 | SequentialAnimation { 85 | SmoothedAnimation { 86 | velocity: listView.highlightMoveVelocity 87 | duration: maxAnimationDuration 88 | } 89 | ScriptAction { 90 | script: { 91 | //print ("Animation stoped, contentX: "+listView.contentX); 92 | behOnContentX.enabled = false; 93 | } 94 | } 95 | } 96 | } 97 | 98 | //-------------------------------------------------------------------------- 99 | Component.onCompleted: { 100 | if (initialIndex > 0 && initialIndex < model.count) { 101 | //contentX = initialIndex*width+originX; 102 | positionViewAtIndex(initialIndex, ListView.Beginning); 103 | //_currentIndex = initialIndex; 104 | } 105 | visible = true; 106 | } 107 | } 108 | 109 | //-------------------------------------------------------------------------- 110 | // Делегат с включённой анимацией 111 | Component { 112 | id: animationDelegateComponent 113 | SwipeAnimationDelegate { } 114 | } 115 | 116 | //-------------------------------------------------------------------------- 117 | onModelChanged: { 118 | //listView._currentIndex = 0; 119 | } 120 | 121 | onInteractiveChanged: { 122 | listView.interactive = interactive; 123 | } 124 | 125 | //-------------------------------------------------------------------------- 126 | // Функции для работы со SwipeView 127 | //-------------------------------------------------------------------------- 128 | // 129 | // Переход к следующему элементу 130 | function incrementCurrentIndex () { 131 | if (!interactive || isMoving) { 132 | return; 133 | } 134 | 135 | if (listView._currentIndex+1 < model.count) { 136 | //listView.isMoving = true; 137 | listView.movedBySwipe = false; 138 | behOnContentX.enabled = true; 139 | listView.contentX += width; 140 | } else { 141 | Log.warning ("It's already at end of SwipeView"); 142 | } 143 | } 144 | 145 | //-------------------------------------------------------------------------- 146 | // Переход к предыдущему элементу 147 | function decrementCurrentIndex () { 148 | if (!interactive || isMoving) { 149 | return; 150 | } 151 | 152 | if (listView._currentIndex >= 1) { 153 | //listView.isMoving = true; 154 | listView.movedBySwipe = false; 155 | behOnContentX.enabled = true; 156 | listView.contentX -= width; 157 | } else { 158 | Log.warning ("It's already at begining of SwipeView"); 159 | } 160 | } 161 | 162 | //-------------------------------------------------------------------------- 163 | // Установка _currentIndex в конкретное значение index. 164 | // Если установлен флаг withAnimation в true, то переход происходит 165 | // с анимацией, иначе - без. 166 | function setCurrentIndex (index, withAnimation/* = false*/) { 167 | if (!interactive || isMoving) { 168 | return; 169 | } 170 | 171 | var contentX = index*width+listView.originX; 172 | 173 | if (listView.contentX !== contentX) { 174 | listView.movedBySwipe = false; 175 | 176 | if (withAnimation !== undefined && withAnimation) { 177 | listView.isMoving = true; 178 | behOnContentX.enabled = true; 179 | } 180 | 181 | listView.contentX = contentX; 182 | } else { 183 | Log.warning ("It's already at this index"); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /qml/TextBig.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import "../" 3 | 4 | Text { 5 | font { 6 | //family: Consts.mainFont.name 7 | pixelSize: Consts.fontBig 8 | letterSpacing: Consts.fontBigLetterSpacing 9 | } 10 | wrapMode: Text.WrapAtWordBoundaryOrAnywhere 11 | height: contentHeight 12 | } 13 | -------------------------------------------------------------------------------- /qml/TextNormal.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import "../" 3 | 4 | Text { 5 | font { 6 | //family: Consts.mainFont.name 7 | pixelSize: Consts.fontNormal 8 | letterSpacing: Consts.fontNormalLetterSpacing 9 | } 10 | 11 | minimumPixelSize: 1 12 | fontSizeMode: Text.HorizontalFit 13 | height: contentHeight 14 | } 15 | -------------------------------------------------------------------------------- /qml/TextSmall.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import "../" 3 | 4 | Text { 5 | font { 6 | //family: Consts.mainFont.name 7 | pixelSize: Consts.fontSmall 8 | letterSpacing: Consts.fontSmallLetterSpacing 9 | } 10 | 11 | wrapMode: Text.WrapAtWordBoundaryOrAnywhere 12 | height: contentHeight 13 | } 14 | -------------------------------------------------------------------------------- /qml/Toast.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import "./" 3 | import "../" 4 | 5 | //------------------------------------------------------------------------------ 6 | /// @brief Объект, для создания всплывающих сообщений 7 | /// Размещать в Root 8 | /// TODO всю схему нужно переделать 9 | /// TODO в данной реализации будет косяк с клавиатурой 10 | //------------------------------------------------------------------------------ 11 | Loader { 12 | id: toast 13 | 14 | property string currentMessage: "" 15 | 16 | y: 0.7*parent.height - 0.5*height 17 | anchors.horizontalCenter: parent.horizontalCenter 18 | opacity: 0 19 | z: 10000 20 | 21 | Behavior on opacity { NumberAnimation { duration: 300 } } 22 | 23 | //-------------------------------------------------------------------------- 24 | Component { 25 | id: visualComponent 26 | 27 | Rectangle { 28 | width : 0.7*Window.width 29 | height: messageText.contentHeight + 2 * Consts.margin 30 | color: "black" 31 | radius: 4*mm 32 | 33 | Text { 34 | id: messageText 35 | text: currentMessage 36 | color: "white" 37 | font.pixelSize: Consts.fontNormal 38 | horizontalAlignment: Text.AlignHCenter 39 | verticalAlignment: Text.AlignVCenter 40 | maximumLineCount: 3 41 | wrapMode: Text.Wrap 42 | anchors { 43 | fill: parent 44 | topMargin: Consts.margin 45 | leftMargin: parent.radius 46 | rightMargin: parent.radius 47 | bottomMargin: Consts.margin 48 | } 49 | } 50 | } 51 | } 52 | 53 | //-------------------------------------------------------------------------- 54 | Timer { 55 | id: closeTimer 56 | running: false 57 | interval: 1500 58 | repeat: false 59 | onTriggered: { 60 | toast.state = "" 61 | } 62 | } 63 | 64 | //-------------------------------------------------------------------------- 65 | states: [ 66 | State { 67 | name: "active" 68 | PropertyChanges { 69 | target: toast 70 | opacity: 0.7 71 | } 72 | PropertyChanges { 73 | target: toast 74 | sourceComponent: visualComponent 75 | restoreEntryValues: false 76 | } 77 | }, 78 | State { 79 | name: "" 80 | PropertyChanges { 81 | target: toast 82 | opacity: 0 83 | } 84 | } 85 | ] 86 | 87 | //-------------------------------------------------------------------------- 88 | // Функция показа всплывающего сообщения 89 | // showTime - время [мс] показа сообщения. По умолчанию 2500 90 | // forceShow - форсировать показ, даже если повторяет предыдущее 91 | //-------------------------------------------------------------------------- 92 | function showMessage(text, showTime, force) { 93 | if (toast.state !== "") { 94 | return 95 | } 96 | 97 | currentMessage = text 98 | toast.state = "active"; 99 | closeTimer.interval = 1500; 100 | if (showTime !== undefined) { 101 | closeTimer.interval = showTime 102 | } 103 | closeTimer.start(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /qml/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | qmldir 4 | ConstsPrototype.qml 5 | Debug.qml 6 | TextBig.qml 7 | TextNormal.qml 8 | TextSmall.qml 9 | ActionBar.qml 10 | DrawerPrototype.qml 11 | ButtonPrototype.qml 12 | MaterialButton.qml 13 | MenuButton.qml 14 | StackView.qml 15 | ShadowBottom.qml 16 | ShadowTop.qml 17 | ActionBarImage.qml 18 | SwipeAnimationDelegate.qml 19 | SwipeView.qml 20 | BackButton.qml 21 | ShadowRectangle.qml 22 | DebugRectangle.qml 23 | SwipeSelector.qml 24 | SmileForRate.qml 25 | Counter.qml 26 | Toast.qml 27 | KeysHandler.qml 28 | DebugTimer.qml 29 | DebugLayout.qml 30 | Zoom.qml 31 | FullScreenImage.qml 32 | FullScreenImageView.qml 33 | 34 | 35 | -------------------------------------------------------------------------------- /qml/qmldir: -------------------------------------------------------------------------------- 1 | module SpQml 2 | singleton Debug 1.0 Debug.qml 3 | -------------------------------------------------------------------------------- /source/Arc.cpp: -------------------------------------------------------------------------------- 1 | #include "Arc.h" 2 | 3 | sp::Arc::Arc(QQuickItem *parent) 4 | : QQuickPaintedItem(parent) 5 | { 6 | setPerformanceHint(QQuickPaintedItem::FastFBOResizing, true); 7 | setAntialiasing(true); // По умолчанию antialiasing включен 8 | } 9 | 10 | //------------------------------------------------------------------------------ 11 | void sp::Arc::classBegin() 12 | { 13 | // Ничего нет 14 | } 15 | 16 | //------------------------------------------------------------------------------ 17 | void sp::Arc::componentComplete() 18 | { 19 | _complete = true; 20 | } 21 | 22 | //------------------------------------------------------------------------------ 23 | void sp::Arc::paint(QPainter *painter) 24 | { 25 | QPen pen; 26 | pen.setStyle(Qt::SolidLine); 27 | pen.setWidth(_penWidth); 28 | pen.setColor(_color); 29 | painter->setPen(pen); 30 | 31 | if (_arcHeight <= 2*_penWidth) { 32 | 33 | painter->drawLine(QLineF (_penWidth 34 | , 0.5*height() 35 | , width() - _penWidth 36 | , 0.5*height() )); 37 | } else { 38 | painter->drawArc(QRectF(_penWidth 39 | , _penWidth + 0.5*(height() - _arcHeight) 40 | , width()-2*_penWidth 41 | , _arcHeight-2*_penWidth 42 | ) 43 | , static_cast(16*_startAngle) 44 | , static_cast(16*_spanAngle)); 45 | } 46 | } 47 | 48 | //------------------------------------------------------------------------------ 49 | double sp::Arc::penWidth() const 50 | { 51 | return _penWidth; 52 | } 53 | 54 | double sp::Arc::arcHeight() const 55 | { 56 | return _arcHeight; 57 | } 58 | 59 | double sp::Arc::startAngle() const 60 | { 61 | return _startAngle; 62 | } 63 | 64 | double sp::Arc::spanAngle() const 65 | { 66 | return _spanAngle; 67 | } 68 | 69 | QColor sp::Arc::color() const 70 | { 71 | return _color; 72 | } 73 | 74 | //------------------------------------------------------------------------------ 75 | void sp::Arc::setPenWidth(double penWidth) 76 | { 77 | if (_penWidth != penWidth) { 78 | _penWidth = penWidth; 79 | emit penWidthChanged(_penWidth); 80 | 81 | if (_complete) { 82 | update(); 83 | } 84 | } 85 | } 86 | 87 | void sp::Arc::setArcHeight(double arcHeight) 88 | { 89 | if (_arcHeight != arcHeight) { 90 | _arcHeight = arcHeight; 91 | emit arcHeightChanged(_arcHeight); 92 | 93 | if (_complete) { 94 | update(); 95 | } 96 | } 97 | } 98 | 99 | void sp::Arc::setStartAngle(double startAngle) 100 | { 101 | if (_startAngle != startAngle) { 102 | _startAngle = startAngle; 103 | emit startAngleChanged(_startAngle); 104 | 105 | if (_complete) { 106 | update(); 107 | } 108 | } 109 | } 110 | 111 | void sp::Arc::setSpanAngle(double spanAngle) 112 | { 113 | if (_spanAngle != spanAngle) { 114 | _spanAngle = spanAngle; 115 | emit spanAngleChanged(_spanAngle); 116 | 117 | if (_complete) { 118 | update(); 119 | } 120 | } 121 | } 122 | 123 | void sp::Arc::setColor(const QColor &color) 124 | { 125 | if (_color != color) { 126 | _color = color; 127 | emit colorChanged(_color); 128 | 129 | if (_complete) { 130 | update(); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /source/ArcFast.cpp: -------------------------------------------------------------------------------- 1 | #include "ArcFast.h" 2 | 3 | #include 4 | #include 5 | 6 | sp::ArcFast::ArcFast(QQuickItem *parent) 7 | : QQuickItem (parent) 8 | { 9 | setFlag(QQuickItem::ItemHasContents); 10 | } 11 | 12 | void sp::ArcFast::classBegin() 13 | { 14 | // Ничего нет 15 | } 16 | 17 | void sp::ArcFast::componentComplete() 18 | { 19 | _complete = true; 20 | } 21 | 22 | //------------------------------------------------------------------------------ 23 | QSGNode* sp::ArcFast::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) 24 | { 25 | QSGGeometryNode *node = 0; 26 | QSGGeometry *geometry = 0; 27 | 28 | if (!oldNode) { 29 | node = new QSGGeometryNode; 30 | geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), _segmentCount); 31 | geometry->setLineWidth(_penWidth); 32 | geometry->setDrawingMode(GL_LINE_STRIP); 33 | node->setGeometry(geometry); 34 | node->setFlag(QSGNode::OwnsGeometry); 35 | 36 | QSGFlatColorMaterial *material = new QSGFlatColorMaterial; 37 | material->setColor(_color); 38 | node->setMaterial(material); 39 | node->setFlag(QSGNode::OwnsMaterial); 40 | } else { 41 | node = static_cast(oldNode); 42 | geometry = node->geometry(); 43 | geometry->allocate(_segmentCount); 44 | } 45 | 46 | QRectF bounds = boundingRect(); 47 | QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D(); 48 | 49 | for (int i = 0; i < _segmentCount; ++i) { 50 | qreal t = i / qreal(_segmentCount - 1); 51 | qreal angle = qDegreesToRadians(_startAngle + _spanAngle*t); 52 | 53 | float x = bounds.x() + (1 + qCos(angle)) * bounds.width()/2; 54 | float y = bounds.y() + (1 - qSin(angle)) * bounds.height()/2; 55 | 56 | vertices[i].set(x, y); 57 | } 58 | node->markDirty(QSGNode::DirtyGeometry); 59 | 60 | return node; 61 | } 62 | 63 | //------------------------------------------------------------------------------ 64 | double sp::ArcFast::penWidth() const 65 | { 66 | return _penWidth; 67 | } 68 | 69 | double sp::ArcFast::startAngle() const 70 | { 71 | return _startAngle; 72 | } 73 | 74 | double sp::ArcFast::spanAngle() const 75 | { 76 | return _spanAngle; 77 | } 78 | 79 | QColor sp::ArcFast::color() const 80 | { 81 | return _color; 82 | } 83 | 84 | int sp::ArcFast::segmentCount() const 85 | { 86 | return _segmentCount; 87 | } 88 | 89 | //------------------------------------------------------------------------------ 90 | void sp::ArcFast::setPenWidth(double penWidth) 91 | { 92 | if (_penWidth != penWidth) { 93 | _penWidth = penWidth; 94 | emit penWidthChanged(_penWidth); 95 | 96 | if (_complete) { 97 | update(); 98 | } 99 | } 100 | } 101 | 102 | void sp::ArcFast::setStartAngle(double startAngle) 103 | { 104 | if (_startAngle != startAngle) { 105 | _startAngle = startAngle; 106 | emit startAngleChanged(_startAngle); 107 | 108 | if (_complete) { 109 | update(); 110 | } 111 | } 112 | } 113 | 114 | void sp::ArcFast::setSpanAngle(double spanAngle) 115 | { 116 | if (_spanAngle != spanAngle) { 117 | _spanAngle = spanAngle; 118 | emit spanAngleChanged(_spanAngle); 119 | 120 | if (_complete) { 121 | update(); 122 | } 123 | } 124 | } 125 | 126 | void sp::ArcFast::setColor(const QColor &color) 127 | { 128 | if (_color != color) { 129 | _color = color; 130 | emit colorChanged(_color); 131 | 132 | if (_complete) { 133 | update(); 134 | } 135 | } 136 | } 137 | 138 | void sp::ArcFast::setSegmentCount(int segmentCount) 139 | { 140 | if (_segmentCount != segmentCount) { 141 | _segmentCount = segmentCount; 142 | emit segmentCountChanged(_segmentCount); 143 | 144 | if (_complete) { 145 | update(); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /source/Arrow.cpp: -------------------------------------------------------------------------------- 1 | #include "Arrow.h" 2 | 3 | #include 4 | #include 5 | 6 | sp::Arrow::Arrow(QQuickItem *parent) 7 | : QQuickItem (parent) 8 | { 9 | setFlag(QQuickItem::ItemHasContents); 10 | } 11 | 12 | void sp::Arrow::classBegin() 13 | { 14 | // Ничего нет 15 | } 16 | 17 | void sp::Arrow::componentComplete() 18 | { 19 | _complete = true; 20 | } 21 | 22 | //------------------------------------------------------------------------------ 23 | QSGNode* sp::Arrow::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) 24 | { 25 | QSGGeometryNode *node = 0; 26 | QSGGeometry *geometry = 0; 27 | 28 | if (!oldNode) { 29 | node = new QSGGeometryNode; 30 | geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), _vertexCount); 31 | geometry->setDrawingMode(QSGGeometry::DrawTriangleFan); 32 | node->setGeometry(geometry); 33 | node->setFlag(QSGNode::OwnsGeometry); 34 | 35 | QSGFlatColorMaterial *material = new QSGFlatColorMaterial; 36 | material->setColor(_color); 37 | node->setMaterial(material); 38 | node->setFlag(QSGNode::OwnsMaterial); 39 | } else { 40 | node = static_cast(oldNode); 41 | geometry = node->geometry(); 42 | geometry->allocate(_vertexCount); 43 | } 44 | 45 | QRectF bounds = boundingRect(); 46 | QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D(); 47 | 48 | QPointF a(bounds.x()+bounds.height()/2 49 | ,bounds.y()); 50 | QPointF b(bounds.x() 51 | ,bounds.y() + bounds.height()/2); 52 | QPointF c(bounds.x() + bounds.height()/2 53 | ,bounds.y() + bounds.height()); 54 | QPointF d(bounds.x() + bounds.width() 55 | ,bounds.y() + bounds.height()/2); 56 | 57 | qreal p = qSin(M_PI/4); 58 | qreal q = 0.5+1/p; 59 | 60 | vertices[0].set(b.x(), b.y()); 61 | vertices[1].set(c.x(), c.y()); 62 | vertices[2].set(c.x()+p*_penWidth 63 | ,c.y()-p*_penWidth); 64 | vertices[3].set(b.x()+q*_penWidth 65 | ,b.y()+0.5*_penWidth); 66 | 67 | vertices[4].set(d.x() 68 | ,d.y()+0.5*_penWidth); 69 | vertices[5].set(d.x() 70 | ,d.y()-0.5*_penWidth); 71 | vertices[6].set(b.x()+q*_penWidth 72 | ,b.y()-0.5*_penWidth); 73 | vertices[7].set(a.x()+p*_penWidth 74 | ,a.y()+p*_penWidth); 75 | vertices[8].set(a.x(), a.y()); 76 | vertices[9].set(b.x(), b.y()); 77 | 78 | node->markDirty(QSGNode::DirtyGeometry); 79 | 80 | return node; 81 | } 82 | 83 | //------------------------------------------------------------------------------ 84 | void sp::Arrow::setPenWidth(double penWidth) 85 | { 86 | if (_penWidth != penWidth) { 87 | _penWidth = penWidth; 88 | emit penWidthChanged(_penWidth); 89 | 90 | if (_complete) { 91 | update(); 92 | } 93 | } 94 | } 95 | 96 | void sp::Arrow::setColor(const QColor &color) 97 | { 98 | if (_color != color) { 99 | _color = color; 100 | emit colorChanged(_color); 101 | 102 | if (_complete) { 103 | update(); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /source/DeviceInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "DeviceInfo.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #if defined(Q_OS_ANDROID) 7 | #include 8 | #include 9 | #include 10 | #endif 11 | 12 | using namespace sp; 13 | //------------------------------------------------------------------------------ 14 | DeviceInfo::DeviceInfo(QObject *parent) 15 | : QObject(parent) 16 | { } 17 | 18 | //------------------------------------------------------------------------------ 19 | QString DeviceInfo::deviceID() 20 | { 21 | QString token = ""; 22 | //#if defined(Q_OS_ANDROID) 23 | // token = QAndroidJniObject::callStaticObjectMethod(QString("sp/SpActivity") 24 | // , "getDeviceID" 25 | // , "(Landroid/content/Context;)Ljava/lang/String;" 26 | // , QtAndroid::androidActivity().object()).toString(); 27 | //#endif 28 | 29 | return token; 30 | //return QString(QCryptographicHash::hash( token.toUtf8(), QCryptographicHash::Sha256 ).toHex() ); 31 | } 32 | 33 | //------------------------------------------------------------------------------ 34 | float DeviceInfo::getAndroidVersion() 35 | { 36 | #ifdef Q_OS_IOS 37 | //return getIosVersion(); 38 | return 7.0; 39 | #elif defined(Q_OS_ANDROID) 40 | // int version = (int)QAndroidJniObject::QAndroidJniObject::callStaticMethod( 41 | // QString("sp/SpActivity") 42 | // ,"getVersion" 43 | // , "()I" ); 44 | // return version; 45 | #endif 46 | return 0; 47 | } 48 | 49 | //------------------------------------------------------------------------------ 50 | int DeviceInfo::availableRam() 51 | { 52 | //#if defined(Q_OS_ANDROID) 53 | // return static_cast 54 | // (QAndroidJniObject::callStaticMethod( 55 | // QString("sp/SpActivity") 56 | // , "availableRAM" 57 | // , "(Landroid/content/Context;)J" 58 | // , QtAndroid::androidActivity().object())); 59 | //#endif 60 | 61 | return 0; 62 | } 63 | 64 | //------------------------------------------------------------------------------ 65 | void DeviceInfo::killApp() 66 | { 67 | LOG_INFO("Завершаем приложение, с кодом 0"); 68 | #ifdef Q_OS_ANDROID 69 | QtAndroid::androidActivity().callMethod("quitApplication"); 70 | #else 71 | qApp->exit(); 72 | #endif 73 | } 74 | 75 | //------------------------------------------------------------------------------ 76 | int DeviceInfo::statusBarHeight() 77 | { 78 | #if defined (Q_OS_ANDROID) 79 | QAndroidJniObject activity = QtAndroid::androidActivity(); 80 | jint height = activity.callMethod("statusBarHeight"); 81 | 82 | return (int) height; 83 | #elif defined (Q_OS_IOS) 84 | //TODO Добавить код для iOS 85 | return 0; 86 | #endif 87 | 88 | #if defined (QT_NO_DEBUG) 89 | return 0; 90 | #else 91 | return 2.5*qApp->mm(); 92 | #endif 93 | } 94 | 95 | //------------------------------------------------------------------------------ 96 | bool DeviceInfo::abilityChangeStatusBar() 97 | { 98 | #if defined (Q_OS_ANDROID) 99 | //Все, что выше KitKat - подходит. 100 | QAndroidJniObject activity = QtAndroid::androidActivity(); 101 | jboolean j_availible = activity.callMethod("availibleChangeStatusBar"); 102 | return j_availible == JNI_TRUE; 103 | #endif 104 | 105 | return true; 106 | } 107 | 108 | //------------------------------------------------------------------------------ 109 | DeviceInfo &DeviceInfo::instance(QObject *parent) 110 | { 111 | static DeviceInfo instance(parent); 112 | return instance; 113 | } 114 | -------------------------------------------------------------------------------- /source/DownloadFileHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "DownloadFileHandler.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace sp; 9 | 10 | //------------------------------------------------------------------------------ 11 | DownloadFileHandler::DownloadFileHandler(const QUrl &url, const QFileInfo &fileName) 12 | : _url(url) 13 | , _fileInfo (fileName) 14 | { 15 | _file = new QFile(_fileInfo.path() % "/~" % _fileInfo.fileName()); 16 | 17 | if (_file->exists()) { 18 | _file->remove(); 19 | } 20 | } 21 | 22 | //------------------------------------------------------------------------------ 23 | void DownloadFileHandler::tuningRequest(QNetworkRequest *request) 24 | { 25 | QByteArray rangeValue = "bytes=" + QByteArray::number(_file->size()) + "-"; 26 | request->setRawHeader("Range", rangeValue); 27 | _needCheckAcceptRanges = true; 28 | } 29 | 30 | //------------------------------------------------------------------------------ 31 | void DownloadFileHandler::onDownloadProgress(quint64 /*bytesReceived*/, quint64 /*bytesTotal*/) 32 | { 33 | QNetworkReply *reply = static_cast(sender()); 34 | DownloadFileHandler *context = static_cast(reply->request().originatingObject()); 35 | 36 | if (!reply->isOpen()) { 37 | // Этот случай возникает, если отменить запрос в onDownloadProgress 38 | return; 39 | } 40 | 41 | if (_needCheckAcceptRanges) { 42 | bool acceptRanges = reply->rawHeader("Content-Range") != ""; 43 | 44 | if (acceptRanges) { 45 | _file->open(QIODevice::Append); 46 | } else { 47 | _file->open(QIODevice::WriteOnly); 48 | } 49 | 50 | _needCheckAcceptRanges = false; 51 | } 52 | 53 | QByteArray buf = reply->readAll(); 54 | context->file()->write(buf); 55 | } 56 | 57 | //------------------------------------------------------------------------------ 58 | void DownloadFileHandler::onFinished() 59 | { 60 | QNetworkReply *reply = static_cast(sender()); 61 | DownloadFileHandler *context = static_cast(reply->request().originatingObject()); 62 | 63 | if (reply->error() == QNetworkReply::NoError) { 64 | context->file()->close(); 65 | if (QFile::exists(context->fileInfo().filePath())) { 66 | QFile::remove(context->fileInfo().filePath()); 67 | } 68 | context->file()->rename(context->fileInfo().filePath()); 69 | emit finished(); 70 | } 71 | } 72 | 73 | //------------------------------------------------------------------------------ 74 | void DownloadFileHandler::onError(QNetworkReply::NetworkError code) 75 | { 76 | QNetworkReply *reply = static_cast(sender()); 77 | DownloadFileHandler *context = static_cast(reply->request().originatingObject()); 78 | 79 | if (code == QNetworkReply::ContentNotFoundError 80 | ||code == QNetworkReply::ContentAccessDenied 81 | ||code == QNetworkReply::ContentOperationNotPermittedError 82 | ||code == QNetworkReply::ProtocolUnknownError 83 | ||code == QNetworkReply::AuthenticationRequiredError) 84 | { 85 | LOG_ERROR("Ошбика загрузки файла. Тупо нельзя его скачать" << reply->url().toString()); 86 | context->file()->close(); 87 | 88 | if (context->file()->exists()) { 89 | context->file()->remove(); 90 | } 91 | 92 | emit error(false); 93 | } else { 94 | if (code != QNetworkReply::OperationCanceledError) { 95 | LOG_ERROR("Некритичная ошибка загрузки файла - повтор" << reply->url().toString()); 96 | } 97 | 98 | context->file()->close(); 99 | emit error(true); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /source/ImageSaverSp.cpp: -------------------------------------------------------------------------------- 1 | #include "ImageSaverSp.h" 2 | #include "Private/ImageSpLoader.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if defined(Q_OS_ANDROID) 10 | #include 11 | #include 12 | #include 13 | #include 14 | #endif 15 | 16 | using namespace sp; 17 | 18 | //---------------------------------------------------------------------------------------------------- 19 | ImageSaverSp::ImageSaverSp(QObject *parent) : 20 | QObject(parent), 21 | pictureLocation(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)) 22 | { 23 | 24 | } 25 | 26 | //---------------------------------------------------------------------------------------------------- 27 | QString ImageSaverSp::save(const QString &imageUrl, const QString &albumName) 28 | { 29 | return saveImage(imageUrl, albumName); 30 | } 31 | 32 | //---------------------------------------------------------------------------------------------------- 33 | bool ImageSaverSp::checkFile(const QString &imageUrl, const QString &albumName) 34 | { 35 | QString fileName = getImageNameFromURL(imageUrl); 36 | QString filePath = pictureLocation +"/" + albumName + fileName; 37 | return QFile::exists(filePath); 38 | } 39 | 40 | //---------------------------------------------------------------------------------------------------- 41 | QString ImageSaverSp::saveImage(const QString &imageUrl, const QString &albumName) 42 | { 43 | QString fileName = getImageNameFromURL(imageUrl); 44 | QString folderPath = pictureLocation + "/" + albumName; 45 | QString filePath = folderPath + "/" + fileName; 46 | bool success = true; 47 | 48 | // Создаем папку если ее нет 49 | QDir dir(folderPath); 50 | if (!dir.exists()){ 51 | success = success && dir.mkdir(folderPath); 52 | } 53 | 54 | // Получаем картинку, которую надо сохранить 55 | QSharedPointer _image(new QImage()); 56 | ImageSpLoader::instance().get(imageUrl, _image); 57 | QImage *image = _image.data(); 58 | 59 | if (_image.isNull()) { 60 | return ""; 61 | } 62 | 63 | success = success && image->save (filePath); 64 | 65 | //Возвращаем путь до созданного файла 66 | QString resultPath = ""; 67 | if (success) { 68 | resultPath = filePath; 69 | #if defined(Q_OS_ANDROID) 70 | androidUpdateGalleryApp(filePath); 71 | #endif 72 | } 73 | 74 | return resultPath; 75 | } 76 | 77 | //---------------------------------------------------------------------------------------------------- 78 | void ImageSaverSp::androidUpdateGalleryApp(const QString& filePath) 79 | { 80 | #if defined(Q_OS_ANDROID) 81 | QAndroidJniObject activity = QtAndroid::androidActivity(); 82 | QAndroidJniObject j_filePath = QAndroidJniObject::fromString(filePath); 83 | activity.callMethod("refreshGallery", "(Ljava/lang/String;)V" 84 | , j_filePath.object()); 85 | #endif 86 | } 87 | 88 | //---------------------------------------------------------------------------------------------------- 89 | QString ImageSaverSp::getImageNameFromURL(const QString &imageURL) 90 | { 91 | QString imageUrl = imageURL.right(imageURL.length()-imageURL.lastIndexOf("/")); 92 | imageUrl = imageUrl.replace(QString("?"),""); 93 | 94 | if (imageUrl.right(4).toLower() != "jpeg" && imageUrl.right(3).toLower() != "jpg") { 95 | imageUrl.append(".jpg"); 96 | } 97 | 98 | return imageUrl; 99 | } 100 | -------------------------------------------------------------------------------- /source/KeyboardSp.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyboardSp.h" 2 | 3 | #include 4 | #include 5 | 6 | #ifdef Q_OS_ANDROID 7 | #include 8 | #include 9 | #endif 10 | 11 | #if defined(Q_OS_IOS) 12 | 13 | #import 14 | 15 | static UIViewController *qtController = nil; 16 | 17 | @interface KeyboardNotificationIos : NSObject {} 18 | 19 | - (id) initNotifications; 20 | - (void) keyboardWillShow:(NSNotification *)note; 21 | - (void) keyboardDidShow:(NSNotification *)note; 22 | - (void) keyboardWillHide:(NSNotification *)note ; 23 | - (void) keyboardDidHide:(NSNotification *)note; 24 | 25 | @end 26 | 27 | @implementation KeyboardNotificationIos 28 | 29 | - (id) initNotifications { 30 | [super init]; 31 | 32 | // При появлении клавиатуры вызывается селектор keyboardWillShow: 33 | [[NSNotificationCenter defaultCenter] addObserver:self 34 | selector:@selector(keyboardWillShow:) 35 | name:@"UIKeyboardWillShowNotification" 36 | object:nil]; 37 | // Клавиатура польностью показана 38 | [[NSNotificationCenter defaultCenter] addObserver:self 39 | selector:@selector(keyboardDidShow:) 40 | name:@"UIKeyboardDidShowNotification" 41 | object:nil]; 42 | // При скрытии клавиатуры 43 | [[NSNotificationCenter defaultCenter] addObserver:self 44 | selector:@selector(keyboardWillHide:) 45 | name:@"UIKeyboardWillHideNotification" 46 | object:nil]; 47 | 48 | [[NSNotificationCenter defaultCenter] addObserver:self 49 | selector:@selector(keyboardDidHide:) 50 | name:@"UIKeyboardDidHideNotification" 51 | object:nil]; 52 | } 53 | 54 | -(void)dealloc 55 | { 56 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 57 | //???? TODO - посмотреть зачем 58 | [self release]; 59 | [super dealloc]; 60 | } 61 | 62 | - (void) keyboardWillShow:(NSNotification *)note { 63 | NSDictionary *userInfo = [note userInfo]; 64 | CGSize kbSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; 65 | // CGSize sizeOfView = self.view.bounds.size; 66 | //NSLog(@"keyboardWillShow");// in Obj - C code height = %f,height = %f", kbSize.height,sizeOfView.height); 67 | // int result = (int)roundf(sizeOfView.height - kbSize.height - 20 ); 68 | 69 | //DEBUG!!! 70 | if (qtController == nil) { 71 | UIWindow *window = [UIApplication sharedApplication].keyWindow; 72 | qtController = [window rootViewController]; 73 | } 74 | 75 | int result = (int)roundf(qtController.view.bounds.size.height - kbSize.height); 76 | sp::KeyboardSp &instance = sp::KeyboardSp::instance(); 77 | instance.sendVisibleChanged(true, result); 78 | } 79 | 80 | - (void) keyboardDidShow:(NSNotification *)note { 81 | #pragma unused(note) 82 | } 83 | 84 | - (void) keyboardWillHide:(NSNotification *)note { 85 | #pragma unused(note) 86 | } 87 | 88 | - (void) keyboardDidHide:(NSNotification *)note { 89 | #pragma unused(note) 90 | int result = (int)roundf(qtController.view.bounds.size.height); 91 | sp::KeyboardSp &instance = sp::KeyboardSp::instance(); 92 | instance.sendVisibleChanged(false, result); 93 | } 94 | @ end 95 | #endif 96 | 97 | //------------------------------------------------------------------------------ 98 | sp::KeyboardSp &sp::KeyboardSp::instance() 99 | { 100 | static KeyboardSp instance; 101 | return instance; 102 | } 103 | 104 | //------------------------------------------------------------------------------ 105 | void sp::KeyboardSp::sendVisibleChanged(bool visible, int height) 106 | { 107 | qDebug() << "sendVisibleChanged! visible = " << visible << " height = " << height; 108 | 109 | KeyboardSp &instance = KeyboardSp::instance(); 110 | 111 | if (qApp) { 112 | if (instance._height != height) { 113 | instance._height = height; 114 | emit instance.heightChanged(instance._height); 115 | } 116 | 117 | if (instance._visible != visible || instance._height != height) { 118 | instance._visible = visible; 119 | emit instance.visibleChanged(instance._visible); 120 | } 121 | } 122 | } 123 | 124 | //------------------------------------------------------------------------------ 125 | sp::KeyboardSp::KeyboardSp() 126 | : QObject() 127 | { 128 | #if defined(Q_OS_IOS) 129 | [[KeyboardNotificationIos alloc] initNotifications]; 130 | #endif 131 | } 132 | 133 | //------------------------------------------------------------------------------ 134 | void sp::KeyboardSp::show() { 135 | #ifdef Q_OS_ANDROID 136 | QAndroidJniObject activity = QtAndroid::androidActivity(); 137 | activity.callMethod("showKeyboard"); 138 | #endif 139 | } 140 | 141 | //------------------------------------------------------------------------------ 142 | void sp::KeyboardSp::hide() { 143 | #ifdef Q_OS_ANDROID 144 | QAndroidJniObject activity = QtAndroid::androidActivity(); 145 | activity.callMethod("hideKeyboard"); 146 | #endif 147 | } 148 | -------------------------------------------------------------------------------- /source/KeyboardSp.mm: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef Q_OS_ANDROID 4 | #include 5 | #include 6 | #endif 7 | #include "KeyboardSp.h" 8 | #include 9 | 10 | #if defined(Q_OS_IOS) 11 | 12 | #import 13 | 14 | static UIViewController *qtController = nil; 15 | 16 | @interface KeyboardNotificationIos : NSObject {} 17 | 18 | - (id) initNotifications; 19 | - (void) keyboardWillShow:(NSNotification *)note; 20 | - (void) keyboardDidShow:(NSNotification *)note; 21 | - (void) keyboardWillHide:(NSNotification *)note ; 22 | - (void) keyboardDidHide:(NSNotification *)note; 23 | 24 | @end 25 | 26 | @implementation KeyboardNotificationIos 27 | 28 | - (id) initNotifications { 29 | [super init]; 30 | 31 | // При появлении клавиатуры вызывается селектор keyboardWillShow: 32 | [[NSNotificationCenter defaultCenter] addObserver:self 33 | selector:@selector(keyboardWillShow:) 34 | name:@"UIKeyboardWillShowNotification" 35 | object:nil]; 36 | // Клавиатура польностью показана 37 | [[NSNotificationCenter defaultCenter] addObserver:self 38 | selector:@selector(keyboardDidShow:) 39 | name:@"UIKeyboardDidShowNotification" 40 | object:nil]; 41 | // При скрытии клавиатуры 42 | [[NSNotificationCenter defaultCenter] addObserver:self 43 | selector:@selector(keyboardWillHide:) 44 | name:@"UIKeyboardWillHideNotification" 45 | object:nil]; 46 | 47 | [[NSNotificationCenter defaultCenter] addObserver:self 48 | selector:@selector(keyboardDidHide:) 49 | name:@"UIKeyboardDidHideNotification" 50 | object:nil]; 51 | } 52 | 53 | -(void)dealloc 54 | { 55 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 56 | //???? TODO - посмотреть зачем 57 | [self release]; 58 | [super dealloc]; 59 | } 60 | 61 | - (void) keyboardWillShow:(NSNotification *)note { 62 | NSDictionary *userInfo = [note userInfo]; 63 | CGSize kbSize = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; 64 | // CGSize sizeOfView = self.view.bounds.size; 65 | //NSLog(@"keyboardWillShow");// in Obj - C code height = %f,height = %f", kbSize.height,sizeOfView.height); 66 | // int result = (int)roundf(sizeOfView.height - kbSize.height - 20 ); 67 | 68 | //DEBUG!!! 69 | if (qtController == nil) { 70 | UIWindow *window = [UIApplication sharedApplication].keyWindow; 71 | qtController = [window rootViewController]; 72 | } 73 | 74 | int result = (int)roundf(qtController.view.bounds.size.height - kbSize.height); 75 | sp::KeyboardSp &instance = sp::KeyboardSp::instance(); 76 | instance.sendVisibleChanged(true, result); 77 | } 78 | 79 | - (void) keyboardDidShow:(NSNotification *)note { 80 | #pragma unused(note) 81 | } 82 | 83 | - (void) keyboardWillHide:(NSNotification *)note { 84 | #pragma unused(note) 85 | } 86 | 87 | - (void) keyboardDidHide:(NSNotification *)note { 88 | #pragma unused(note) 89 | int result = (int)roundf(qtController.view.bounds.size.height); 90 | sp::KeyboardSp &instance = sp::KeyboardSp::instance(); 91 | instance.sendVisibleChanged(false, result); 92 | } 93 | @ end 94 | #endif 95 | 96 | //------------------------------------------------------------------------------ 97 | sp::KeyboardSp &sp::KeyboardSp::instance() 98 | { 99 | static KeyboardSp instance; 100 | return instance; 101 | } 102 | 103 | //------------------------------------------------------------------------------ 104 | void sp::KeyboardSp::sendVisibleChanged(bool visible, int height) 105 | { 106 | qDebug() << "sendVisibleChanged! visible = " << visible << " height = " << height; 107 | 108 | KeyboardSp &instance = KeyboardSp::instance(); 109 | 110 | if (qApp) { 111 | if (instance._height != height) { 112 | instance._height = height; 113 | emit instance.heightChanged(instance._height); 114 | } 115 | 116 | if (instance._visible != visible || instance._height != height) { 117 | instance._visible = visible; 118 | emit instance.visibleChanged(instance._visible); 119 | } 120 | } 121 | } 122 | 123 | //------------------------------------------------------------------------------ 124 | sp::KeyboardSp::KeyboardSp() 125 | : QObject() 126 | { 127 | #if defined(Q_OS_IOS) 128 | [[KeyboardNotificationIos alloc] initNotifications]; 129 | #endif 130 | } 131 | 132 | //------------------------------------------------------------------------------ 133 | void sp::KeyboardSp::show() { 134 | #ifdef Q_OS_ANDROID 135 | QAndroidJniObject activity = QtAndroid::androidActivity(); 136 | activity.callMethod("showKeyboard"); 137 | #endif 138 | } 139 | 140 | //------------------------------------------------------------------------------ 141 | void sp::KeyboardSp::hide() { 142 | #ifdef Q_OS_ANDROID 143 | QAndroidJniObject activity = QtAndroid::androidActivity(); 144 | activity.callMethod("hideKeyboard"); 145 | #endif 146 | } 147 | -------------------------------------------------------------------------------- /source/LogSp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef Q_OS_ANDROID 4 | #include 5 | #include 6 | #endif 7 | 8 | #include "LogSp.h" 9 | 10 | using namespace sp; 11 | 12 | Log Log::_instance; 13 | QTime Log::_time; 14 | QStringList Log::_logArchive; 15 | 16 | //------------------------------------------------------------------------------ 17 | Log::Log(QObject *parent) 18 | :QObject (parent) 19 | { 20 | _time.start(); 21 | qInstallMessageHandler(sp::Log::messageHandler); 22 | } 23 | 24 | //------------------------------------------------------------------------------ 25 | Log &Log::instance() 26 | { 27 | return _instance; 28 | } 29 | 30 | void Log::debug(const QString &str) 31 | { 32 | LOG_DEBUG(str); 33 | } 34 | 35 | void Log::info(const QString &str) 36 | { 37 | LOG_INFO(str); 38 | } 39 | 40 | void Log::error(const QString &str) 41 | { 42 | LOG_ERROR(str); 43 | } 44 | 45 | void Log::warning(const QString &str) 46 | { 47 | LOG_WARN(str); 48 | } 49 | 50 | void Log::fatal(const QString &str) 51 | { 52 | LOG_FATAL(str); 53 | } 54 | 55 | void Log::aleus(const QString &str) 56 | { 57 | #ifdef SP_ALEUS 58 | LOG_ALEUS(str); 59 | #endif 60 | } 61 | 62 | void Log::v(const QString &str) 63 | { 64 | #ifdef SP_VONABIRG 65 | LOG_V(str); 66 | #endif 67 | } 68 | 69 | //-------------------------------------------------------------------------- 70 | QString Log::logArchive() const { 71 | return _logArchive.join('\n'); 72 | } 73 | 74 | //------------------------------------------------------------------------------ 75 | void Log::toArchive(const QString &str) 76 | { 77 | _logArchive.append(str); 78 | } 79 | 80 | //------------------------------------------------------------------------------ 81 | void Log::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) 82 | { 83 | QString formatedStr = 84 | #ifdef QT_MESSAGELOGCONTEXT 85 | context.file != nullptr && (type == QtCriticalMsg || type == QtFatalMsg) 86 | ? QString::number(_time.elapsed()) + ": [" % QString(context.file) % ":" % QString::number(context.line) % ", " % context.function % "] " % message % '\n' 87 | : 88 | #endif 89 | QString::number(_time.elapsed()) % ": " % message % '\n'; 90 | 91 | #ifdef Q_OS_ANDROID 92 | android_LogPriority priority = ANDROID_LOG_INFO; 93 | switch (type) { 94 | case QtWarningMsg: 95 | priority = ANDROID_LOG_WARN; 96 | break; 97 | case QtCriticalMsg: 98 | priority = ANDROID_LOG_ERROR; 99 | break; 100 | case QtFatalMsg: 101 | priority = ANDROID_LOG_FATAL; 102 | break; 103 | 104 | }; 105 | __android_log_print(priority, "#######", "%s", formatedStr.toLocal8Bit().constData()); 106 | 107 | #else 108 | QByteArray localMsg = message.toLocal8Bit(); 109 | fprintf(stderr, formatedStr.toUtf8().constData()); 110 | #endif 111 | 112 | _instance.toArchive(formatedStr); 113 | } 114 | 115 | #pragma GCC diagnostic pop 116 | -------------------------------------------------------------------------------- /source/Net.cpp: -------------------------------------------------------------------------------- 1 | #include "Net.h" 2 | #include "LogSp.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace sp; 9 | 10 | const int MAX_ACTIVE_HANDLERS = 5; 11 | 12 | //------------------------------------------------------------------------------ 13 | Net::Net() 14 | : QObject(nullptr) 15 | { 16 | _nam.moveToThread(&_thread); 17 | moveToThread(&_thread); 18 | _thread.start(); 19 | 20 | connect (this, &Net::makeRequest, this, &Net::onMakeRequest); 21 | connect (this, &Net::abortRequest, this, &Net::onAbortRequest); 22 | connect (&_nam, &QNetworkAccessManager::networkAccessibleChanged, this, &Net::onNetworkAccessibilityChanged); 23 | } 24 | 25 | //------------------------------------------------------------------------------ 26 | Net &Net::instance() 27 | { 28 | static Net net; 29 | return net; 30 | } 31 | 32 | /***************************************************************************//** 33 | * @brief Загружает файл с сети. Сохраняется в текущю папку. 34 | * @param url адрес файл 35 | * @param fileName имя файла 36 | * @return Возвращает обработчик загрузки файла 37 | ******************************************************************************/ 38 | DownloadFileHandler* Net::downloadFile(const QString &url, const QString &fileName) 39 | { 40 | DownloadFileHandler *handler = new DownloadFileHandler(url, fileName); 41 | emit instance().makeRequest(handler); 42 | 43 | return handler; 44 | } 45 | 46 | /***************************************************************************//** 47 | * @brief Загружает файл с сети. Сохраняется в текущю папку. 48 | * @param url адрес файл 49 | * @param fileName имя файла 50 | * @return Возвращает обработчик загрузки файла 51 | ******************************************************************************/ 52 | DownloadFileHandler* Net::downloadFile(const QString &url, const QFileInfo &fileName) 53 | { 54 | DownloadFileHandler *handler = new DownloadFileHandler(url, fileName); 55 | emit instance().makeRequest(handler); 56 | 57 | return handler; 58 | } 59 | 60 | /***************************************************************************//** 61 | * @brief Загружает файл с сети. Сохраняется в текущю папку. Имя файла определяется из url'а. 62 | * @param url адрес файл 63 | * @return Возвращает обработчик загрузки файла 64 | ******************************************************************************/ 65 | DownloadFileHandler *Net::downloadFile(const QString &url) 66 | { 67 | QFileInfo fileInfo(url); 68 | DownloadFileHandler *handler = new DownloadFileHandler(url, fileInfo.fileName()); 69 | emit instance().makeRequest(handler); 70 | 71 | return handler; 72 | } 73 | 74 | //------------------------------------------------------------------------------ 75 | void Net::onNetworkAccessibilityChanged(QNetworkAccessManager::NetworkAccessibility accessible) 76 | { 77 | if (accessible == QNetworkAccessManager::Accessible) { 78 | onMakeRequest(); 79 | } 80 | } 81 | 82 | //-------------------------------------------------------------------------- 83 | void Net::onMakeRequest(NetHandler *handler/* = nullptr*/) 84 | { 85 | while (_activeHandler.count() < MAX_ACTIVE_HANDLERS && _nam.networkAccessible() == QNetworkAccessManager::Accessible) { 86 | if (!handler) { 87 | if (!_handlersQueue.isEmpty()) { 88 | handler = _handlersQueue.dequeue(); 89 | } else { 90 | // Очередь запрос пуста 91 | return; 92 | } 93 | } 94 | 95 | _activeHandler << handler; 96 | 97 | QNetworkRequest request; 98 | request.setUrl(QUrl(handler->url())); 99 | request.setOriginatingObject(handler); 100 | request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); 101 | handler->tuningRequest(&request); 102 | 103 | QNetworkReply *reply = _nam.get(request); 104 | handler->setReply(reply); 105 | 106 | connect (reply, &QNetworkReply::downloadProgress, handler, &NetHandler::onDownloadProgress); 107 | connect (reply, &QNetworkReply::finished, handler, &NetHandler::onFinished); 108 | connect (reply, SIGNAL(error(QNetworkReply::NetworkError)), handler, SLOT(onError(QNetworkReply::NetworkError))); 109 | 110 | connect (handler, &NetHandler::finished, this, &Net::onNetHandlerFinished); 111 | connect (handler, &NetHandler::error, this, &Net::onNetHandlerError); 112 | 113 | handler = nullptr; 114 | } // while (_activeHandler.count() < MAX_ACTIVE_HANDLERS) { 115 | 116 | // Если уже выполняется максимальное количество запросов 117 | if (handler) { 118 | _handlersQueue << handler; 119 | } 120 | } 121 | 122 | //------------------------------------------------------------------------------ 123 | void Net::onNetHandlerFinished() 124 | { 125 | NetHandler *handler = static_cast(sender()); 126 | _activeHandler.remove(handler); 127 | handler->reply()->deleteLater(); 128 | 129 | onMakeRequest(); 130 | } 131 | 132 | //------------------------------------------------------------------------------ 133 | void Net::onNetHandlerError(bool needRetry) 134 | { 135 | NetHandler *handler = static_cast(sender()); 136 | _activeHandler.remove(handler); 137 | 138 | if (needRetry) { 139 | onMakeRequest(handler); 140 | } else { 141 | handler->reply()->deleteLater(); 142 | } 143 | } 144 | 145 | //------------------------------------------------------------------------------ 146 | void Net::onAbortRequest(NetHandler *handler) 147 | { 148 | if (_activeHandler.contains(handler)) { 149 | _activeHandler.remove(handler); 150 | handler->reply()->abort(); 151 | handler->reply()->deleteLater(); 152 | } 153 | 154 | if (_handlersQueue.contains(handler)) { 155 | _handlersQueue.removeAll(handler); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /source/Settings.cpp: -------------------------------------------------------------------------------- 1 | /// @author M. Serebrennikov 2 | #include "Settings.h" 3 | #include "SpApplicationPrototype.h" 4 | 5 | #include 6 | 7 | using namespace sp; 8 | 9 | Settings &Settings::instance() 10 | { 11 | static Settings instance; 12 | return instance; 13 | } 14 | 15 | //------------------------------------------------------------------------------ 16 | Settings::Settings() 17 | : _settings(QSettings::IniFormat, QSettings::UserScope, "SP", qApp->applicationName()) 18 | { 19 | } 20 | 21 | //------------------------------------------------------------------------------ 22 | Settings::~Settings() 23 | { 24 | _settings.sync(); 25 | } 26 | 27 | //------------------------------------------------------------------------------ 28 | void Settings::onSet(const QString &key, const QVariant &value) 29 | { 30 | _settings.setValue(key, value); 31 | _settings.sync(); 32 | } 33 | 34 | //------------------------------------------------------------------------------ 35 | QVariant Settings::onGet(const QString &key, const QVariant &defaultValue) const 36 | { 37 | if (_settings.contains(key)) { 38 | QVariant v = _settings.value(key); 39 | 40 | if (v.canConvert()) { 41 | return v; 42 | } else { 43 | v.convert(defaultValue.type()); 44 | return v; 45 | } 46 | } else { 47 | return defaultValue; 48 | } 49 | } 50 | 51 | //------------------------------------------------------------------------------ 52 | void Settings::set(const QString &key, const QVariant &value) 53 | { 54 | return instance().onSet(key, value); 55 | } 56 | 57 | //------------------------------------------------------------------------------ 58 | QVariant Settings::get(const QString &key, const QVariant &defaultValue) 59 | { 60 | return instance().onGet(key, defaultValue); 61 | } 62 | -------------------------------------------------------------------------------- /source/Shadow.cpp: -------------------------------------------------------------------------------- 1 | #include "Shadow.h" 2 | #include "SpApplicationPrototype.h" 3 | 4 | sp::Shadow::Shadow(QQuickItem *parent) 5 | : QQuickPaintedItem(parent) 6 | { 7 | setPerformanceHint(QQuickPaintedItem::FastFBOResizing, true); 8 | setAntialiasing(true); // По умолчанию antialiasing включен 9 | } 10 | 11 | //------------------------------------------------------------------------------ 12 | void sp::Shadow::classBegin() 13 | { 14 | } 15 | 16 | //------------------------------------------------------------------------------ 17 | void sp::Shadow::componentComplete() 18 | { 19 | _complete = true; 20 | } 21 | 22 | //------------------------------------------------------------------------------ 23 | void sp::Shadow::paint(QPainter *painter) 24 | { 25 | QLinearGradient gradient; 26 | 27 | gradient.setStart(width(),height()); 28 | gradient.setFinalStop(0,0); 29 | 30 | gradient.setColorAt((qreal)1, QColor("#00000000") ); 31 | gradient.setColorAt((qreal)0, _color ); 32 | 33 | painter->setBrush(QBrush(gradient)); 34 | painter->setPen(QPen(Qt::NoPen)); 35 | 36 | QRectF rect (QPointF(0,0), QPointF(width(),height())); 37 | qreal rH = qApp->mm() / height() * 100 * 2; 38 | qreal rW = qApp->mm() / width() * 100 * 2; 39 | painter->drawRoundedRect(rect, rW, rH, Qt::RelativeSize); 40 | 41 | // Вариант с рисование неполного Rectangle. Надо подумать. 42 | //QPainterPath path; 43 | //qreal r = height(); // радиус скругления 44 | //path.moveTo(r, 0); 45 | //path.lineTo(width() - r, 0); 46 | //path.arcTo(width() - 2*r, -r , 2*r,2*r 47 | // , 0 48 | // , -90 ); 49 | //path.lineTo(r, r); 50 | // 51 | //path.moveTo(r, 0); 52 | // 53 | //path.arcTo(0, -r , 2*r,2*r 54 | // , 180 55 | // , 90 ); 56 | // 57 | //painter->drawPath(path); 58 | } 59 | 60 | void sp::Shadow::setColor(const QColor &color) 61 | { 62 | if (_color != color) { 63 | _color = color; 64 | emit colorChanged(_color); 65 | 66 | if (_complete) { 67 | update(); 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /source/SpApplicationPrototype.cpp: -------------------------------------------------------------------------------- 1 | #include "SpApplicationPrototype.h" 2 | 3 | #include "Arc.h" 4 | #include "ArcFast.h" 5 | #include "Arrow.h" 6 | #include 7 | #include "ImageSp.h" 8 | #include "LogSp.h" 9 | #include "DeviceInfo.h" 10 | #include "ImageParallax.h" 11 | #include "KeyboardSp.h" 12 | #include "Shadow.h" 13 | #include "ImageSaverSp.h" 14 | #include "Settings.h" 15 | 16 | #if defined(Q_OS_ANDROID) 17 | #include "JniSetup.h" 18 | #endif 19 | 20 | using namespace sp; 21 | 22 | //-------------------------------------------------------------------------- 23 | sp::SpApplicationPrototype::SpApplicationPrototype(int &argc, char **argv, const QString &title, int width, int height) 24 | : QGuiApplication (argc, argv) 25 | , _view () 26 | { 27 | QObject::connect(_view.engine(), SIGNAL(quit()), this, SLOT(quit())); 28 | _view.setTitle(title); 29 | _view.setColor(Qt::white); 30 | 31 | #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) 32 | _view.setWidth(primaryScreen()->size().width()); 33 | _view.setHeight(primaryScreen()->size().height()); 34 | #else 35 | _view.setWidth(width); 36 | _view.setHeight(height); 37 | #endif 38 | 39 | // Включаем антиалиасинг. 40 | #if !defined(Q_OS_LINUX) // Под линуксом на старющей видеокарте не работает setSamples 16. 41 | QSurfaceFormat format = _view.format(); 42 | format.setSamples(16); 43 | _view.setFormat(format); 44 | #else 45 | QSurfaceFormat format = _view.format(); 46 | format.setSamples(8); 47 | _view.setFormat(format); 48 | #endif 49 | 50 | // Регистрируем C++ класса в QML 51 | qmlRegisterType("SP", 1, 0, "Arc"); 52 | qmlRegisterType("SP", 1, 0, "ArcFast"); 53 | qmlRegisterType("SP", 1, 0, "Arrow"); 54 | qmlRegisterType("SP", 1, 0, "ImageSp"); 55 | qmlRegisterType("SP", 1, 0, "ImageParallax"); 56 | qmlRegisterType("SP", 1, 0, "Shadow"); 57 | 58 | // Создаём объекты QML 59 | const double mmInInch = 25.4; 60 | _mm = screens().first()->physicalDotsPerInch()/mmInInch; 61 | 62 | _view.rootContext()->setContextProperty("mm", _mm); 63 | _view.rootContext()->setContextProperty("Window", &_view); 64 | _view.rootContext()->setContextProperty("imageSaverSp", new ImageSaverSp()); 65 | _view.rootContext()->setContextProperty("Log", &sp::Log::instance()); 66 | _view.rootContext()->setContextProperty("deviceInfo", &sp::DeviceInfo::instance()); 67 | _view.rootContext()->setContextProperty("KeyboardSp", &sp::KeyboardSp::instance()); 68 | _view.rootContext()->setContextProperty("Settings", &sp::Settings::instance()); 69 | 70 | #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) 71 | _view.rootContext()->setContextProperty("isDesktop", false); 72 | #else 73 | _view.rootContext()->setContextProperty("isDesktop", true); 74 | #endif 75 | 76 | } 77 | 78 | //-------------------------------------------------------------------------- 79 | int sp::SpApplicationPrototype::exec(const QUrl &source) 80 | { 81 | _view.setSource(source); // по умолчанию "qrc:/Root.qml" 82 | 83 | // Открываем основной файл 84 | #ifdef Q_OS_IOS 85 | _view.showFullScreen(); 86 | #else 87 | _view.show(); 88 | #endif 89 | 90 | return QGuiApplication::exec(); 91 | } 92 | -------------------------------------------------------------------------------- /source/SpApplicationPrototype.mm: -------------------------------------------------------------------------------- 1 | #include "SpApplicationPrototype.h" 2 | 3 | #include "Arc.h" 4 | #include "ArcFast.h" 5 | #include "Arrow.h" 6 | #include 7 | #include "ImageSp.h" 8 | #include "LogSp.h" 9 | #include "DeviceInfo.h" 10 | #include "ImageParallax.h" 11 | #include "KeyboardSp.h" 12 | #include "Shadow.h" 13 | #include "ImageSaverSp.h" 14 | #include "Settings.h" 15 | 16 | #if defined(Q_OS_ANDROID) 17 | #include "JniSetup.h" 18 | #elif defined(Q_OS_IOS) 19 | #import 20 | #endif 21 | 22 | using namespace sp; 23 | 24 | //-------------------------------------------------------------------------- 25 | sp::SpApplicationPrototype::SpApplicationPrototype(int &argc, char **argv, const QString &title, int width, int height) 26 | : QGuiApplication (argc, argv) 27 | , _view () 28 | { 29 | QObject::connect(_view.engine(), SIGNAL(quit()), this, SLOT(quit())); 30 | _view.setTitle(title); 31 | _view.setColor(Qt::white); 32 | 33 | #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) 34 | _view.setWidth(primaryScreen()->size().width()); 35 | _view.setHeight(primaryScreen()->size().height()); 36 | #else 37 | _view.setWidth(width); 38 | _view.setHeight(height); 39 | #endif 40 | 41 | // Включаем антиалиасинг. 42 | #if !defined(Q_OS_LINUX) // Под линуксом на старющей видеокарте не работает setSamples 16. 43 | QSurfaceFormat format = _view.format(); 44 | format.setSamples(16); 45 | _view.setFormat(format); 46 | #else 47 | QSurfaceFormat format = _view.format(); 48 | format.setSamples(8); 49 | _view.setFormat(format); 50 | #endif 51 | 52 | // Регистрируем C++ класса в QML 53 | qmlRegisterType("SP", 1, 0, "Arc"); 54 | qmlRegisterType("SP", 1, 0, "ArcFast"); 55 | qmlRegisterType("SP", 1, 0, "Arrow"); 56 | qmlRegisterType("SP", 1, 0, "ImageSp"); 57 | qmlRegisterType("SP", 1, 0, "ImageParallax"); 58 | qmlRegisterType("SP", 1, 0, "Shadow"); 59 | 60 | // Создаём объекты QML 61 | const double mmInInch = 25.4; 62 | _mm = screens().first()->physicalDotsPerInch()/mmInInch; 63 | 64 | _view.rootContext()->setContextProperty("mm", _mm); 65 | _view.rootContext()->setContextProperty("Window", &_view); 66 | _view.rootContext()->setContextProperty("imageSaverSp", new ImageSaverSp()); 67 | _view.rootContext()->setContextProperty("Log", &sp::Log::instance()); 68 | _view.rootContext()->setContextProperty("deviceInfo", &sp::DeviceInfo::instance()); 69 | _view.rootContext()->setContextProperty("KeyboardSp", &sp::KeyboardSp::instance()); 70 | _view.rootContext()->setContextProperty("Settings", &sp::Settings::instance()); 71 | 72 | #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) 73 | _view.rootContext()->setContextProperty("isDesktop", false); 74 | #else 75 | _view.rootContext()->setContextProperty("isDesktop", true); 76 | #endif 77 | 78 | } 79 | 80 | //-------------------------------------------------------------------------- 81 | int sp::SpApplicationPrototype::exec(const QUrl &source) 82 | { 83 | _view.setSource(source); // по умолчанию "qrc:/Root.qml" 84 | 85 | // Открываем основной файл 86 | #ifdef Q_OS_IOS 87 | _view.showFullScreen(); 88 | [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; 89 | #else 90 | _view.show(); 91 | #endif 92 | 93 | return QGuiApplication::exec(); 94 | } 95 | -------------------------------------------------------------------------------- /source/private/ImageSpLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "Private/ImageSpLoader.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace sp; 10 | 11 | #ifdef __clang__ 12 | # pragma clang diagnostic push 13 | # pragma clang diagnostic ignored "-Wmissing-variable-declarations" 14 | #endif 15 | int __idImageSharedPtr = qRegisterMetaType("ImageSharedPtr"); 16 | int __idImageWeakPtr = qRegisterMetaType("ImageWeakPtr"); 17 | #ifdef __clang__ 18 | # pragma clang diagnostic pop 19 | #endif 20 | 21 | //------------------------------------------------------------------------------ 22 | ImageSpLoader::ImageSpLoader() 23 | : QObject (nullptr) 24 | { 25 | moveToThread(&_thread); 26 | connect (this, SIGNAL(loadTo(const QString&, ImageSharedPtr)), SLOT(get(const QString&, ImageSharedPtr))); 27 | _thread.start(); 28 | } 29 | 30 | //------------------------------------------------------------------------------ 31 | ImageSpLoader& ImageSpLoader::instance() 32 | { 33 | static ImageSpLoader instance; 34 | return instance; 35 | } 36 | 37 | //------------------------------------------------------------------------------ 38 | void ImageSpLoader::get(const QString &source, ImageSharedPtr image) 39 | { 40 | if (source.startsWith("qrc:/")) { 41 | if (image->load(source.mid(3))) { 42 | emit loaded(source, image); 43 | } else { 44 | LOG_ERROR(QString("Ошибка загрузки изображения: %1").arg(source)); 45 | emit error(source, image, QString("Ошибка загрузки изображения: %1").arg(source)); 46 | } 47 | return; 48 | } 49 | 50 | *image = QImage(); 51 | emit error(source, image, "Неизвестный способ загрузки изображения."); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /source/private/ImageSpNode.cpp: -------------------------------------------------------------------------------- 1 | #include "Private/ImageSpNode.h" 2 | #include "SpApplicationPrototype.h" 3 | 4 | #include 5 | 6 | //------------------------------------------------------------------------------ 7 | sp::ImageSpNode::ImageSpNode(int vertexAtCorner) 8 | : _vertexAtCorner(vertexAtCorner) 9 | , _segmentCount (4*_vertexAtCorner+3) 10 | { 11 | QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), _segmentCount); 12 | geometry->setDrawingMode(QSGGeometry::DrawTriangleFan); 13 | setGeometry(geometry); 14 | 15 | setFlag(QSGNode::OwnsGeometry); 16 | setFlag(QSGNode::OwnsOpaqueMaterial); 17 | } 18 | 19 | //------------------------------------------------------------------------------ 20 | sp::ImageSpNode::~ImageSpNode() 21 | { 22 | if (opaqueMaterial()) { 23 | static_cast(material())->texture()->deleteLater(); 24 | } 25 | } 26 | 27 | //------------------------------------------------------------------------------ 28 | void sp::ImageSpNode::setImage(const QImage &image) 29 | { 30 | QSGTexture *texture; 31 | 32 | if (material()) { 33 | texture = qApp->view()->createTextureFromImage(image, QQuickWindow::TextureIsOpaque); 34 | auto *material = static_cast(this->material()); 35 | material->texture()->deleteLater(); 36 | material->setTexture(texture); 37 | } else { 38 | texture = qApp->view()->createTextureFromImage(image); 39 | auto *material = new QSGOpaqueTextureMaterial; 40 | material->setTexture(texture); 41 | material->setFiltering(QSGTexture::Linear); 42 | material->setMipmapFiltering(QSGTexture::Linear); 43 | 44 | setMaterial(material); 45 | } 46 | } 47 | 48 | //------------------------------------------------------------------------------ 49 | void sp::ImageSpNode::setMipmap(bool mipmap) 50 | { 51 | _mipmap = mipmap; 52 | 53 | auto *material = static_cast(this->material()); 54 | if (material) { 55 | material->setMipmapFiltering(_mipmap ? QSGTexture::Linear : QSGTexture::None); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /source/private/NetHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "Private/NetHandler.h" 2 | #include "Net.h" 3 | 4 | using namespace sp; 5 | 6 | NetHandler::NetHandler() 7 | { 8 | moveToThread(Net::thread()); 9 | } 10 | 11 | void NetHandler::setReply(QNetworkReply *reply) 12 | { 13 | _reply = reply; 14 | } 15 | -------------------------------------------------------------------------------- /sp_qt_libs.pri: -------------------------------------------------------------------------------- 1 | QT += widgets 2 | INCLUDEPATH += $$PWD/include 3 | HEADERS += \ 4 | $$PWD/include/Settings.h \ 5 | $$PWD/include/Arc.h \ 6 | $$PWD/include/ArcFast.h \ 7 | $$PWD/include/SpApplicationPrototype.h \ 8 | $$PWD/include/LogSp.h \ 9 | $$PWD/include/ImageSp.h \ 10 | $$PWD/include/Priavte/ImageSpNode.h \ 11 | $$PWD/include/Private/ImageSpLoader.h \ 12 | $$PWD/include/ImageSaverSp.h \ 13 | $$PWD/include/ImageParallax.h \ 14 | $$PWD/include/DeviceInfo.h \ 15 | $$PWD/include/KeyboardSp.h \ 16 | $$PWD/include/Arrow.h \ 17 | $$PWD/include/Shadow.h \ 18 | $$PWD/include/Net.h \ 19 | $$PWD/include/DownloadFileHandler.h \ 20 | $$PWD/include/Private/NetHandler.h 21 | 22 | SOURCES += \ 23 | $$PWD/source/Settings.cpp \ 24 | $$PWD/source/Arc.cpp \ 25 | $$PWD/source/ArcFast.cpp \ 26 | $$PWD/source/LogSp.cpp \ 27 | $$PWD/source/ImageSp.cpp \ 28 | $$PWD/source/Private/ImageSpNode.cpp \ 29 | $$PWD/source/Private/ImageSpLoader.cpp \ 30 | $$PWD/source/ImageSaverSp.cpp \ 31 | $$PWD/source/ImageParallax.cpp \ 32 | $$PWD/source/DeviceInfo.cpp \ 33 | $$PWD/source/Arrow.cpp \ 34 | $$PWD/source/Shadow.cpp \ 35 | $$PWD/source/Net.cpp \ 36 | $$PWD/source/DownloadFileHandler.cpp \ 37 | $$PWD/source/Private/NetHandler.cpp 38 | 39 | # Внешние ресурсы 40 | RESOURCES += \ 41 | $$PWD/qml/qml.qrc \ 42 | 43 | android { 44 | QT += androidextras 45 | 46 | DISTFILES += \ 47 | $$PWD/android/src/sp/SpActivity.java 48 | } 49 | 50 | !mac { 51 | SOURCES += \ 52 | $$PWD/source/SpApplicationPrototype.cpp \ 53 | $$PWD/source/KeyboardSp.cpp 54 | } 55 | 56 | mac { 57 | OBJECTIVE_SOURCES += \ 58 | $$PWD/source/SpApplicationPrototype.mm \ 59 | $$PWD/source/KeyboardSp.mm 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /sp_qt_libs.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | import qbs.FileInfo 3 | 4 | Product { 5 | name: "sp_qt_libs" 6 | type: "staticlibrary" 7 | version: "1.0" 8 | 9 | files: [ 10 | "include/Arc.h", 11 | "include/ArcFast.h", 12 | "include/Arrow.h", 13 | "include/DeviceInfo.h", 14 | "include/DownloadFileHandler.h", 15 | "include/ImageParallax.h", 16 | "include/ImageSaverSp.h", 17 | "include/ImageSp.h", 18 | "include/KeyboardSp.h", 19 | "include/LogSp.h", 20 | "include/Net.h", 21 | "include/Private/ImageSpLoader.h", 22 | "include/Private/ImageSpNode.h", 23 | "include/Private/NetHandler.h", 24 | "include/Settings.h", 25 | "include/Shadow.h", 26 | "include/SpApplicationPrototype.h", 27 | "source/Arc.cpp", 28 | "source/ArcFast.cpp", 29 | "source/Arrow.cpp", 30 | "source/DeviceInfo.cpp", 31 | "source/DownloadFileHandler.cpp", 32 | "source/ImageParallax.cpp", 33 | "source/ImageSaverSp.cpp", 34 | "source/ImageSp.cpp", 35 | "source/KeyboardSp.cpp", 36 | "source/LogSp.cpp", 37 | "source/Net.cpp", 38 | "source/Private/ImageSpLoader.cpp", 39 | "source/Private/ImageSpNode.cpp", 40 | "source/Private/NetHandler.cpp", 41 | "source/Settings.cpp", 42 | "source/Shadow.cpp", 43 | "source/SpApplicationPrototype.cpp", 44 | "qml/qml.qrc", 45 | ] 46 | 47 | cpp.includePaths: [path + "/include"] 48 | cpp.cxxLanguageVersion: "c++11" 49 | qbs.architecture: "x86_64" 50 | 51 | Depends { name: "Qt"; submodules: ["core", "network", "qml", "quick" ] } 52 | Depends { name: "cpp" } 53 | 54 | Group { 55 | name: "install" 56 | fileTagsFilter: product.type 57 | qbs.install: true 58 | qbs.installPrefix: "lib" 59 | qbs.installSourceBase: qbs.buildDirectory 60 | } 61 | 62 | Properties { 63 | condition: qbs.targetOS.contains("macos") 64 | // Грязный хак для доступа к приватным классам QtPlatformHeaders. Используется в ObjCFunctions.mm 65 | cpp.includePaths: [ 66 | Qt.core.libPath, 67 | Qt.core.libPath + "/QtGui.framework/Versions/" + Qt.core.versionMajor + "/Headers/" + Qt.core.version 68 | ] 69 | cpp.minimumMacosVersion: "10.7" 70 | } 71 | 72 | Export { 73 | Depends { name: "cpp" } 74 | cpp.includePaths: [path + "/include"] 75 | } 76 | } 77 | --------------------------------------------------------------------------------