Commit 6e8fc4dc authored by Aleksi Suomalainen's avatar Aleksi Suomalainen

Merge pull request #27 from faenil/header

[header] New header component and big changes to orientation handling
parents f36a19f5 458b27fb
......@@ -64,7 +64,10 @@ Item {
text: modelData
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 30
anchors.leftMargin: 20
anchors.right: arrow.left
anchors.rightMargin: 20
clip: true
}
Rectangle {
......@@ -76,6 +79,7 @@ Item {
}
Image {
id: arrow
anchors.right: parent.right
anchors.rightMargin: 20
anchors.verticalCenter: parent.verticalCenter
......@@ -86,6 +90,5 @@ Item {
id: mouse
anchors.fill: parent
onClicked: root.clicked()
}
}
......@@ -46,7 +46,8 @@ import QtQuick.Controls.Styles.Nemo 1.0
Page {
id: root
tools: ToolBarLayoutExample { title: "Buttons" }
headerTools: HeaderToolsLayout { showBackButton: false; title: "Buttons (portrait only, no back arrow)" }
allowedOrientations: Qt.PortraitOrientation
Column {
spacing: 40
......
......@@ -37,7 +37,8 @@ import QtQuick.Controls.Styles.Nemo 1.0
Page {
id: root
tools: ToolBarLayoutExample { title: "ButtonRow" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Button row (Landscape only)" }
allowedOrientations: Qt.LandscapeOrientation
Column {
spacing: 40
......
......@@ -37,7 +37,7 @@ import QtQuick.Controls.Styles.Nemo 1.0
Page {
id: root
tools: ToolBarLayoutExample { title: "Switch" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Switch" }
Column {
spacing: 40
......
......@@ -37,7 +37,8 @@ import QtQuick.Controls.Styles.Nemo 1.0
Page {
id: root
tools: ToolBarLayoutExample { title: "Label" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Label" }
allowedOrientations: Qt.PortraitOrientation | Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation | Qt.InvertedPortraitOrientation
Column {
spacing: 40
......
......@@ -28,7 +28,7 @@ Page {
property var oldItem
property var newItem
tools: ToolBarLayoutExample { title: "Live Coding Arena" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Live Coding Arena" }
SplitView {
anchors.fill: parent
......
......@@ -62,7 +62,7 @@ Page {
}
}
tools: ToolBarLayoutExample { title: "Progress Bars" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Progress Bars" }
Column {
spacing: 40
......
......@@ -37,7 +37,7 @@ import QtQuick.Controls.Styles.Nemo 1.0
Page {
id: root
tools: ToolBarLayoutExample { title: "Query dialog example" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Query dialog example" }
QueryDialog {
cancelText: "Cancel"
......
......@@ -46,7 +46,7 @@ import QtQuick.Controls.Styles.Nemo 1.0
Page {
id: root
tools: ToolBarLayoutExample { title: "Sliders" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Sliders" }
Column {
spacing: 12
......
......@@ -37,7 +37,7 @@ import QtQuick.Controls.Styles.Nemo 1.0
Page {
id: root
tools: ToolBarLayoutExample { title: "Spinner" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Spinner" }
Column {
spacing: 40
......
......@@ -46,7 +46,7 @@ import QtQuick.Controls.Styles.Nemo 1.0
Page {
id: root
tools: ToolBarLayoutExample { title: "Tab bars" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Tab bars" }
TabView {
anchors.fill: parent
......
......@@ -62,7 +62,7 @@ Page {
}
}
tools: ToolBarLayoutExample { title: "Text input" }
headerTools: HeaderToolsLayout { showBackButton: true; title: "Text input" }
Column {
spacing: 40
......
import QtQuick 2.0
import QtQuick.Controls.Nemo 1.0
Item {
id: toolsLayoutItem
anchors.fill: parent
property string title: ""
property StackView pageStack: findStackView(toolsLayoutItem)
//XXX: TEMPORARY CODE, MIGHT CAUSE LAG WHEN PUSHING A PAGE ON THE STACK
function findStackView(startingItem) {
var myStack = startingItem
while (myStack) {
if (myStack.hasOwnProperty("currentItem") && myStack.hasOwnProperty("initialItem"))
return myStack
myStack = myStack.parent
}
return null
}
Rectangle {
id: backButton
width: opacity ? 60 : 0
anchors.left: parent.left
anchors.leftMargin: 20
//check if Stack.view has already been initialized as well
opacity: (pageStack && (pageStack.depth > 1)) ? 1 : 0
anchors.verticalCenter: parent.verticalCenter
antialiasing: true
height: 60
radius: 4
color: backmouse.pressed ? "#222" : "transparent"
Behavior on opacity { NumberAnimation{} }
Image {
anchors.verticalCenter: parent.verticalCenter
source: "../images/navigation_previous_item.png"
}
MouseArea {
id: backmouse
anchors.fill: parent
anchors.margins: -10
onClicked: pageStack.pop()
}
}
Label {
font.pixelSize: 42
Behavior on x { NumberAnimation { easing.type: Easing.OutCubic } }
x: backButton.x + backButton.width + 20
anchors.verticalCenter: parent.verticalCenter
text: parent.title
}
}
......@@ -43,6 +43,7 @@ import QtQuick.Controls 1.0
import QtQuick.Controls.Nemo 1.0
import QtQuick.Controls.Styles.Nemo 1.0
import QtQuick.Window 2.1
import QtQuick.Layouts 1.0
import "content"
ApplicationWindow {
......@@ -68,7 +69,7 @@ ApplicationWindow {
page: "content/LiveCoding.qml"
}
ListElement {
title: "Buttons"
title: "Buttons (locked to portrait)"
page: "content/ButtonPage.qml"
}
ListElement {
......@@ -92,7 +93,7 @@ ApplicationWindow {
page: "content/SpinnerPage.qml"
}
ListElement {
title: "Labels"
title: "Labels (no orientation locks)"
page: "content/LabelPage.qml"
}
ListElement {
......@@ -100,7 +101,7 @@ ApplicationWindow {
page: "content/CheckboxPage.qml"
}
ListElement {
title: "ButtonRow"
title: "ButtonRow (locked to landscape)"
page: "content/ButtonRowPage.qml"
}
ListElement {
......@@ -113,7 +114,98 @@ ApplicationWindow {
initialPage: Page {
id: pageItem
tools: ToolBarLayoutExample { title: "Touch gallery" }
headerTools: HeaderToolsLayout {
id: tools
title: "Nemo Touch Gallery"
tools: [ ToolButton { iconSource: "../images/icon_cog.png"},
ToolButton { iconSource: "../images/icon_edit.png"},
ToolButton { iconSource: "../images/icon_refresh.png"} ]
//The parent of these items is null when this ToolsLayout is not used
//(i.e. you're on a different page) so we need to check the parent,
//just like in MeeGo's ToolbarLayout (when you don't use the automatic layout)
//TODO: Add automatic layout logic (see ToolbarLayout in MeeGo)
drawerLevels: [
Button {
anchors.horizontalCenter: (parent==undefined) ? undefined : parent.horizontalCenter;
text: "Nemo"
},
Button {
anchors.horizontalCenter: (parent==undefined) ? undefined : parent.horizontalCenter;
text: "Mobile"
},
Button {
anchors.horizontalCenter: (parent==undefined) ? undefined : parent.horizontalCenter;
text: "FTW"
},
RowLayout {
anchors.left: (parent==undefined) ? undefined : parent.left
anchors.right: (parent==undefined) ? undefined : parent.right
anchors.margins: 20
Layout.preferredHeight: 100
Label {
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter
text: "Drawer Test"}
CheckBox {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width : 20
}
},
RowLayout {
anchors.left: (parent==undefined) ? undefined : parent.left
anchors.right: (parent==undefined) ? undefined : parent.right
anchors.margins: 20
Layout.preferredHeight: 100
Label {
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter
text: "Drawer Test 2"}
ToolButton {
id: tool1
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
iconSource: "../images/icon_cog.png"
}
ToolButton {
id: tool2
anchors.right: tool1.left
anchors.verticalCenter: parent.verticalCenter
iconSource: "../images/icon_edit.png"
}
ToolButton {
id: tool3
anchors.right: tool2.left
anchors.verticalCenter: parent.verticalCenter
iconSource: "../images/icon_refresh.png"
}
},
ButtonRow {
id: buttonRowExample
model: ListModel {
ListElement {
name: "swim"
}
ListElement {
name: "cruise"
}
ListElement {
name: "row"
}
ListElement {
name: "fish"
}
ListElement {
name: "dive"
}
}
}
]
}
ListView {
model: pageModel
......@@ -126,5 +218,3 @@ ApplicationWindow {
}
}
}
......@@ -16,11 +16,15 @@
<file>images/textinput.png</file>
<file>images/toolbar.png</file>
<file>content/LiveCoding.qml</file>
<file>content/ToolBarLayoutExample.qml</file>
<file>content/SpinnerPage.qml</file>
<file>content/LabelPage.qml</file>
<file>content/CheckboxPage.qml</file>
<file>content/ButtonRowPage.qml</file>
<file>content/QueryDialogPage.qml</file>
<file>images/dots-vertical.png</file>
<file>images/icon-triangle-left.png</file>
<file>images/icon_cog.png</file>
<file>images/icon_edit.png</file>
<file>images/icon_refresh.png</file>
</qresource>
</RCC>
......@@ -18,7 +18,6 @@ OTHER_FILES += \
content/TabBarPage.qml \
content/TextInputPage.qml \
content/LiveCoding.qml \
content/ToolBarLayoutExample.qml \
content/SpinnerPage.qml \
content/LabelPage.qml \
content/CheckboxPage.qml \
......
......@@ -29,8 +29,9 @@ NemoWindow {
id: root
width: 320
height: 240
height: 640
property alias header: toolBar
/*! \internal */
default property alias data: contentArea.data
......@@ -38,13 +39,15 @@ NemoWindow {
property alias initialPage: stackView.initialItem
property alias orientation: contentArea.uiOrientation
readonly property int isUiPortrait: orientation == Qt.PortraitOrientation || orientation == Qt.InvertedPortraitOrientation
//is this safe? can there be some situation in which it's neither portrait nor landscape?
readonly property int isUiLandscape: !isUiPortrait
readonly property var _bgColor: Theme.window.background
color: _bgColor
readonly property int defaultAllowedOrientations: Qt.PortraitOrientation | Qt.LandscapeOrientation
//these are application-wise allowed orientations, i.e. what is used if the current Page doesn't set any
allowedOrientations: defaultAllowedOrientations
//README: allowedOrientations' default value is set in NemoWindow's c++ implementation
//The app developer can overwrite it from QML
onAllowedOrientationsChanged: {
orientationConstraintsChanged()
......@@ -61,8 +64,20 @@ NemoWindow {
//if the current orientation is not allowed anymore, fallback to an allowed one
//stackInitialized check prevents setting an orientation before the stackview
//(but more importantly the initialItem of the stack) has been created
if (stackView.stackInitialized && !isOrientationAllowed(contentArea.filteredOrientation)) {
fallbackToAnAllowedOrientation()
if (stackView.stackInitialized) {
//- This is to give priority to the current screen orientation
//- This case happens when, for example, you're on a landscape only page,
// the phone is in portrait, and a page allowing portrait mode is pushed on the stack.
// When the page is pushed, we don't get any orientationChanged signal from the Screen element
// because the phone was already held in portrait mode, se we have to enforce it here.
if (isOrientationAllowed(Screen.orientation)) {
contentArea.filteredOrientation = Screen.orientation
}
//- If neither the current screen orientation nor the one which the UI is already presented in (filteredOrientation)
// are allowed, then fallback to an allowed orientation.
else if (!isOrientationAllowed(contentArea.filteredOrientation)) {
fallbackToAnAllowedOrientation()
}
}
}
......@@ -127,6 +142,8 @@ NemoWindow {
id: backgroundItem
anchors.centerIn: parent
// NOTE: Using Screen.height/width will cause issues when the app is not fullscreen (e.g. when testing using Qt desktop)
// because in that case the app window will be smaller but the content will still be for fullscreen size
width: __transpose ? Screen.height : Screen.width
height: __transpose ? Screen.width : Screen.height
rotation: rotationToTransposeToPortrait()
......@@ -153,9 +170,12 @@ NemoWindow {
StackView {
id: stackView
width: parent.width
height: parent.height
anchors.top: root.isUiPortrait ? toolBar.bottom : parent.top
anchors.right: parent.right
anchors.left: root.isUiPortrait ? parent.left : toolBar.right
anchors.bottom: parent.bottom
clip: true
Component.onCompleted: stackInitialized = true
//IMPORTANT: this property makes it so that at app startup we wait for the initial page to be pushed
//before determining the initial ui orientation (see the states logic below)
......@@ -182,8 +202,10 @@ NemoWindow {
}
//update orientation constraints when a Page is pushed/popped
onCurrentItemChanged: if (_isCurrentItemNemoPage() && currentItem.allowedOrientations)
root.orientationConstraintsChanged()
onCurrentItemChanged: {
if (_isCurrentItemNemoPage())
root.orientationConstraintsChanged()
}
//This properties are accessible for free by the Page via Stack.view.<property>
readonly property int orientation: contentArea.uiOrientation
......@@ -241,6 +263,44 @@ NemoWindow {
}
}
Header {
id: toolBar
stackView: root.pageStack
appWindow: root
//used to animate the dimmer when pages are pushed/popped (see Header's QML code)
property alias __dimmer: headerDimmerContainer
}
Item {
//This item handles the rotation of the dimmer.
//All this because QML doesn't have a horizontal gradient (unless you import GraphicalEffects)
//and having a container which doesn't rotate but just resizes makes it easier to rotate its inner
//child
id: headerDimmerContainer
//README: Don't use AnchorChanges for this item!
//Reason: state changes disable bindings while the transition from one state to another is running.
//This causes the dimmer not to follow the drawer when the drawer is closed right before the orientation change
anchors.top: isUiPortrait ? toolBar.bottom : parent.top
anchors.left: isUiPortrait ? parent.left : toolBar.right
anchors.right: isUiPortrait ? parent.right : undefined
anchors.bottom: isUiPortrait ? undefined : parent.bottom
//we only set the size in one orientation, the anchors will take care of the other
width: if (!isUiPortrait) Theme.header.dimmer.height
height: if (isUiPortrait) Theme.header.dimmer.height
//MAKE SURE THAT THE HEIGHT SPECIFIED BY THE THEME IS AN EVEN NUMBER, TO AVOID ROUNDING ERRORS IN THE LAYOUT
Rectangle {
id: headerDimmer
anchors.centerIn: parent
gradient: Gradient {
GradientStop { position: Theme.header.dimmer.startPosition; color: Theme.header.dimmer.startColor }
GradientStop { position: Theme.header.dimmer.endPosition; color: Theme.header.dimmer.endColor }
}
}
}
Item {
id: orientationState
......@@ -262,6 +322,12 @@ NemoWindow {
rotation: 0
uiOrientation: Qt.PortraitOrientation
}
PropertyChanges {
target: headerDimmer
height: Theme.header.dimmer.height
width: parent.width
rotation: 0
}
},
State {
name: 'Landscape'
......@@ -274,6 +340,12 @@ NemoWindow {
rotation: 90
uiOrientation: Qt.LandscapeOrientation
}
PropertyChanges {
target: headerDimmer
height: Theme.header.dimmer.height
width: parent.height
rotation: -90
}
},
State {
name: 'PortraitInverted'
......@@ -286,6 +358,12 @@ NemoWindow {
rotation: 180
uiOrientation: Qt.InvertedPortraitOrientation
}
PropertyChanges {
target: headerDimmer
height: Theme.header.dimmer.height
width: parent.width
rotation: 0
}
},
State {
name: 'LandscapeInverted'
......@@ -298,6 +376,12 @@ NemoWindow {
rotation: 270
uiOrientation: Qt.InvertedLandscapeOrientation
}
PropertyChanges {
target: headerDimmer
height: Theme.header.dimmer.height
width: parent.height
rotation: -90
}
}
]
......@@ -320,6 +404,13 @@ NemoWindow {
target: contentArea
properties: 'width,height,rotation,uiOrientation'
}
AnchorAnimation {
duration: 0
}
PropertyAction {
target: headerDimmer
properties: 'width,height,rotation'
}
NumberAnimation {
target: contentArea
property: 'opacity'
......
import QtQuick 2.1
import QtQuick.Window 2.0
import QtQuick.Controls 1.0
import QtQuick.Controls.Nemo 1.0
import QtQuick.Controls.Styles.Nemo 1.0
import QtQuick.Layouts 1.0
import QtGraphicalEffects 1.0
Item {
id: root
//TODO: Add logic/animations to handle dynamic change of tools and drawer levels in the same page
//make sure the header is aligned properly
Binding on y {
when: !appWindow.isUiPortrait
value: 0
}
Binding on x {
when: appWindow.isUiPortrait
value: 0
}
//Since the header drawer behaves differently in portrait/landscape modes
//we close the drawer when the UI rotates
Connections {
target: appWindow
onIsUiPortraitChanged: closeDrawer()
}
//Handle portrait/landscape orientation layout changes
//Using states is better than having "anchors.left: appWindow.isUiPortrait ? portraitanchor : landscapeanchor"
//because in the latter way when isUiPortrait changes, the order of reevaluation of the anchors bindings
//(hence the resulting layout) cannot be predicted. Using States we avoid that source of issues
states: [
State {
id: portraitState
when: appWindow && appWindow.isUiPortrait
AnchorChanges { target: toolBarRect; anchors.left: root.left}
AnchorChanges {
target: drawerContainer;
anchors.top: undefined
anchors.bottom: toolBarRect.top
anchors.left: root.left
anchors.right: root.right
}
AnchorChanges {
target: drawer;
anchors.right: undefined
anchors.bottom: drawerContainer.bottom
anchors.verticalCenter: undefined
anchors.horizontalCenter: drawerContainer.horizontalCenter
}
//having width/height as PropertyChanges avoids creating binding loops
PropertyChanges {
target: root
width: parent.width
//the height of the drawer in portrait is limited by the size of the shorter edge of the screen
height: (toolBarRect.height + Math.min(drawer.height, appWindow.__transpose ? Screen.height : Screen.width))
}
//remember: the PropertyChanges handle bindings by default, unless "explicit: true" is set
PropertyChanges {
target: toolBarRect
width: parent.width
height: 75
}
},
State {
id: landscapeState
when: appWindow && !appWindow.isUiPortrait
AnchorChanges { target: toolBarRect; anchors.left: undefined }
AnchorChanges {
target: drawerContainer;
anchors.top: root.top
anchors.bottom: root.bottom
anchors.left: undefined
anchors.right: toolBarRect.left
}
AnchorChanges {
target: drawer;
anchors.right: drawerContainer.right
anchors.bottom: undefined
anchors.verticalCenter: drawerContainer.verticalCenter
anchors.horizontalCenter: undefined
}
PropertyChanges {
target: root
//the width of the drawer in landscape is limited by the size of the shorter edge of the screen
width: (toolBarRect.width + Math.min(drawer.width, appWindow.__transpose ? Screen.height : Screen.width))
height: parent.height
}
PropertyChanges {
target: toolBarRect
width: 75
height: parent.height
}
}
]
//this is the value that will be used by toolBarContainer
//please pretty please don't turn it into an alias to toolBarContainer.data,
//because in that way you won't get change signals (.data doesn't have NOTIFY
//and is a Q_PRIVATE_PROPERTY) and you won't be able to access the toolbarLayout
//directly. So -> not a good idea :)
property variant toolBarLayout
function closeDrawer() {
x = Qt.binding(function() { return appWindow.isUiPortrait ? 0 : -drawer.width})
y = Qt.binding(function() { return appWindow.isUiPortrait ? -drawer.height : 0})
}
property double speedBumpThreshold: 3/5
property int closedY: - drawer.height
property int closedX: - drawer.width
property int closedCoord: appWindow.isUiPortrait ? closedY : closedX
//those properties are initialized when the header is created
//(See ApplicationWindow source)
property StackView stackView
property variant appWindow
//used for manual type checking when looking for items of type Header from other QML elements
property bool __isNemoHeader
//propagate header reference to headerTools which have the "header" property
//used by the headerTools to know when it should display the back button
//(i.e. to get header.stackView.depth value)
Connections {
target: stackView
onCurrentItemChanged: {
if (changeToolsLayoutAnim.running) {
changeToolsLayoutAnim.complete()
}
changeToolsLayoutAnim.start()
}
//Close drawer if a page transition is starting
onBusyChanged: if (stackView.busy) closeDrawer()
}
function propagateHeaderReference() {
if (toolBarLayout && toolBarLayout.hasOwnProperty("header")) {
toolBarLayout.header = toolBar
}
}
function updateHeaderTools() {
root.toolBarLayout = stackView.currentItem ? stackView.currentItem.headerTools : undefined
}
SequentialAnimation {
id: changeToolsLayoutAnim
NumberAnimation { id: fadeOut; target: root; property: "opacity"; to: 0;
duration: Theme.pageStack.transitionDuration/2; easing.type: Easing.OutQuad; loops: !toolBarLayout ? 0 : 1 }
//headerTools may change now, so we have to close the drawer
ScriptAction { script: closeDrawer() }
ScriptAction { script: updateHeaderTools() }
//tell the (maybe new) layout that we're its container
ScriptAction { script: propagateHeaderReference() }
NumberAnimation { id: fadeIn; target: root; property: "opacity"; to: 1;
duration: Theme.pageStack.transitionDuration/2; easing.type: Easing.OutQuad; loops: !toolBarLayout ? 0 : 1 }
}
NumberAnimation {
id: slidingAnim
target: root
property: appWindow.isUiPortrait ? "y" : "x"
from: appWindow.isUiPortrait ? root.y : root.x
easing.type: Easing.OutExpo
}
function slideDrawerTo(coord) {
slidingAnim.to = coord
slidingAnim.start()
}
//THIS ITEM AND ITS CHILDREN ARE THE TOOLBAR (i.e. NOT what's inside the drawer!)
//This item only resizes when UI rotates, while toolBarContainer is the one actually rotating
Rectangle {
id: toolBarRect
anchors.bottom: parent.bottom
anchors.right: parent.right
//README: the rest of the anchors/sizes will be set by AnchorChanges!
color: Theme.header.background
FilteringMouseArea {
id: mouseArea
anchors.fill: parent
property bool swiping: false
property int swipeThreshold: 10
property int gestureThreshold: drawer.width / 3 //this is only used in landscape
//container coordinate (x OR y, depending on UI orientation) relative to the parent
//of the header
property int startCoord: 0
//mouse coordinate (x OR y, depending on UI orientation) relative to the parent
//of the header
property int startMouseCoord: 0
property int deltaCoord: 0
//This is item which holds the toolbar and rotates with the UI
Item {
id: toolBarContainer
//no anchors here, as we're using the rotation to change between portrait/landscape modes
width: appWindow.isUiPortrait ? parent.width : parent.height
height: appWindow.isUiPortrait ? parent.height : parent.width
anchors.centerIn: parent
data: toolBarLayout ? toolBarLayout : null
rotation: appWindow.isUiPortrait ? 0 : -90
}
onPressed: {
if (swiping) {
console.log("[HEADER] Swiping enabled in onPressed, report this.")
swiping = false
}
if (appWindow.isUiPortrait) {
startMouseCoord = (pos.y + root.y)
startCoord = root.y
} else { //assuming that otherwise we're in landscape...is this safe?
startMouseCoord = (pos.x + root.x)
startCoord = root.x
}
}
onPositionChanged: {
if (appWindow.isUiPortrait) {
deltaCoord = (pos.y + root.y) - startMouseCoord
if (Math.abs(deltaCoord) > swipeThreshold && !swiping) { grabMouseEvents(); swiping = true; }
if (swiping) {
var swipingY = startCoord + deltaCoord
if ( swipingY > 0) {
root.y = Math.sqrt(swipingY)
} else {
if (swipingY < root.closedY) root.y = root.closedY
else root.y = swipingY
}
}
} else {
deltaCoord = (pos.x + root.x) - startMouseCoord
if (Math.abs(deltaCoord) > swipeThreshold && !swiping) { grabMouseEvents(); swiping = true; }
if (swiping) {
//this is the coord that the drawer would be at if it were following our finger
var swipingX = startCoord + deltaCoord
//if the left/top side of the drawer is entering the screen, pull the breaks!
//quadratic slowdown effect
if ( swipingX > 0) {
root.x = Math.sqrt(swipingX)
} else {
//don't let the toolbar go out of screen
if (swipingX < root.closedX) root.x = root.closedX
else root.x = swipingX
}
}
}
}
onReleased: {
if (appWindow.isUiPortrait) {
if (!swiping) {
//Fully Close/Open the drawer
root.slideDrawerTo((root.y == root.closedY) ? 0 : root.closedY)
} else {
//this is the y at the top of the screen, relative to the header dock (root)
var topY = -root.y
//We cannot use QML's childAt because it requires both x and y
//and we don't have an x to provide (and it wouldn't make sense to have one,
//given the current specced behaviour of the header)
var item = drawer.getItemAt(topY)
//item is undefined when there's no item at the y of the top edge of the screen
//(this happens for example when you pull down the drawer so that its y is > 0)
if (item == undefined) {
//if the drawer is closed or half open, we open it totally.
//if it was opened already, we close it ("pull down to close" feature)
root.slideDrawerTo(startCoord == 0 ? root.closedY : 0)
} else {
root.slideDrawerTo(((topY - item.y) <= (speedBumpThreshold * item.height)) ? -item.y : -(item.y + item.height))
}
}
} else {
if (!swiping) {
//Fully Close/Open the drawer
root.slideDrawerTo((root.x == root.closedX) ? 0 : root.closedX)
} else {
deltaCoord = (pos.x + root.x) - startMouseCoord
if (deltaCoord > gestureThreshold) {
root.slideDrawerTo(startCoord < 0 ? 0 : closedX)
} else if (deltaCoord < -gestureThreshold){
root.slideDrawerTo(closedX)
} else { //i.e (-gestureThreshold <= deltaCoord <= gestureThreshold)
root.slideDrawerTo((startCoord === root.closedX) ? root.closedX : 0)
}
}
}
swiping = false
}
}
}
Rectangle {
id: drawerContainer
color: Theme.header.background
Binding on width {
value: drawer.width
when: !appWindow.isUiPortrait
}
Binding on height {
value: drawer.height
when: appWindow.isUiPortrait
}
ColumnLayout {
id: drawer
//NOTE: if you set the spacing to something != 0 then you have to rewrite the logic which handles drawer speedbumps,
//which currently relies on "spacing" being 0
spacing: 0
function getItemAt(y) {
//skip the first child, which is the toolbar
for (var i=0; i < children.length; i++) {
if (y >=children[i].y && y <= (children[i].y + children[i].height))
return children[i]
}
}
children: if (toolBarLayout) toolBarLayout.drawerLevels
}
}
}
import QtQuick 2.0
import QtQuick.Controls.Nemo 1.0
import QtQuick.Controls.Styles.Nemo 1.0
import QtQuick.Layouts 1.0
import QtGraphicalEffects 1.0
//This item handles the UI representation for the toolbar
//The UI representation of the drawerLevels is inside the header
//(we may consider having a DrawerLayout element in the future)
Item {
id: toolsLayoutItem
anchors.fill: parent
property alias title: titleTxt.text
//these have to be initialized when the HeaderToolsLayout is instantiated
property Header header
property list<Item> tools
property list<Item> drawerLevels
//we'll get rid of this once we'll have the appWindow accessible everywhere
property bool isUiPortrait: header && header.appWindow.isUiPortrait
property bool showBackButton: false
Rectangle {
id: backButton
width: opacity ? 60 : 0
anchors.leftMargin: 20
//check if Stack.view has already been initialized as well
anchors.verticalCenter: parent.verticalCenter
antialiasing: true
height: width
radius: 4
color: backmouse.pressed ? "#222" : "transparent"
rotation: isUiPortrait ? 0 : 90
visible: showBackButton
Image {
anchors.centerIn: parent
source: "../Styles/Nemo/images/icon-triangle-left.png"
}
MouseArea {
id: backmouse
anchors.fill: parent
anchors.margins: -10
onClicked: header && header.stackView && header.stackView.pop()
}
}
Label {
id: titleTxt
anchors.right: toolButtonsContainer.left
anchors.left: backButton.visible ? backButton.right : parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 20
anchors.rightMargin: 20
clip: true
font.family: Theme.fontFamily
color: Theme.label.color
font.pointSize: 24
font.weight: Font.Bold
LinearGradient {
anchors.right: parent.right
width: 50
height: parent.paintedHeight
visible: titleTxt.paintedWidth > titleTxt.width
start: Qt.point(0,0)
end: Qt.point(width,0)
gradient: Gradient { GradientStop { position: 0; color: "transparent"}
GradientStop {position: 0.9; color: Theme.header.background } }
}
}
Item {
id: toolButtonsContainer
anchors.right: dots.visible ? dots.left : parent.right
anchors.rightMargin: 20
anchors.verticalCenter: parent.verticalCenter
width: tools ? (50 * Math.min(maxNumberOfToolButtons, tools.length)) : 0
property int maxNumberOfToolButtons: 3
RowLayout {
id: toolsRow
anchors.centerIn: parent
function assignRotationBindings() {
for (var i=0; i<children.length; ++i) {
children[i].rotation = Qt.binding(function() { return isUiPortrait ? 0 : 90 })
}
}
//TODO: THIS IS STUPID :D This is run once every added item (i.e. EVEN IF you add 3 items at the same time)
//but it's not critical since it will always have a very limited amount of children
onChildrenChanged: {
assignRotationBindings()
}
children: tools
}
}
Image {
id: dots
anchors.right: parent.right
anchors.rightMargin: 20
anchors.verticalCenter: parent.verticalCenter
visible: drawerLevels && drawerLevels.length > 1
source: "../Styles/Nemo/images/dots-vertical.png"
rotation: isUiPortrait ? 0 : 90
}
}
......@@ -43,13 +43,9 @@ NemoPage {
property alias color: background.color
property int status: pageStack ? Stack.status : Stack.Inactive
property alias tools: toolBar.data
property alias __dimmer: dimmer
property variant headerTools
readonly property StackView pageStack: Stack.view
//TODO: ADD TOOLBARLAYOUT COMPONENT SO THAT USER WILL USE tools: ToolbarLayout { .... }
//instead of tools: [ ... , ... ]
//Children of "page" will be automatically reparented to "content"
default property alias __content: content.data
......@@ -77,31 +73,8 @@ NemoPage {
color: Theme.page.background
}
ToolBar {
id: toolBar
z: 201
}
Item {
id: content
anchors.bottom: parent.bottom
anchors.top: toolBar.bottom
anchors.right: parent.right
anchors.left: parent.left
}
Rectangle {
id: dimmer
height: Theme.page.dimmer.height
anchors.top: toolBar.bottom
anchors.left: parent.left
anchors.right: parent.right
gradient: Gradient {
GradientStop { position: Theme.page.dimmer.startPosition; color: Theme.page.dimmer.startColor }
GradientStop { position: Theme.page.dimmer.endPosition; color: Theme.page.dimmer.endColor }
}
anchors.fill: parent
}
}
......@@ -13,25 +13,27 @@ QML_FILES += \
Spinner.qml \
Label.qml \
Checkbox.qml\
images/disabled-overlay.png
images/disabled-overlay-inverse.png
ButtonRow.qml \
QueryDialog.qml \
Header.qml \
HeaderToolsLayout.qml
OTHER_FILES += qmldir \
$$QML_FILES \
ButtonRow.qml \
QueryDialog.qml
$$QML_FILES
HEADERS += \
qquicknemocontrolsextensionplugin.h \
hacks.h \
nemowindow.h \
nemopage.h
nemopage.h \
qquickfilteringmousearea.h
SOURCES += \
qquicknemocontrolsextensionplugin.cpp \
hacks.cpp \
nemowindow.cpp \
nemopage.cpp
nemopage.cpp \
qquickfilteringmousearea.cpp
target.path = $$[QT_INSTALL_QML]/$$PLUGIN_IMPORT_PATH
......
......@@ -32,5 +32,5 @@ bool Hacks::isOrientationMaskValid(Qt::ScreenOrientations orientations) {
Qt::ScreenOrientations max = (Qt::PortraitOrientation | Qt::LandscapeOrientation
| Qt::InvertedPortraitOrientation | Qt::InvertedLandscapeOrientation);
return (orientations <= max && orientations != 0);
return (orientations <= max && orientations > 0);
}
......@@ -2,7 +2,11 @@
#include "hacks.h"
NemoPage::NemoPage(QQuickItem *parent) :
QQuickItem(parent)
QQuickItem(parent),
m_allowedOrientations(0) //- The value 0 means Page's allowedOrientations will be ignored
//- The value 0 can't be set from QML on purpose (see Hacks::isOrientationMaskValid impl.),
// so that we can use the value 0 to know that the app developer has not touched this value
// (in fact, as just said, once it's changed from QML, the app dev can't set it back to 0 from QML)
{
}
......@@ -14,8 +18,12 @@ Qt::ScreenOrientations NemoPage::allowedOrientations() const
void NemoPage::setAllowedOrientations(Qt::ScreenOrientations allowed)
{
//This way no invalid values can get assigned to allowedOrientations
if (m_allowedOrientations != allowed && Hacks::isOrientationMaskValid(allowed)) {
m_allowedOrientations = allowed;
emit allowedOrientationsChanged();
if (m_allowedOrientations != allowed) {
if (Hacks::isOrientationMaskValid(allowed)) {
m_allowedOrientations = allowed;
emit allowedOrientationsChanged();
} else {
qDebug() << "NemoPage: invalid allowedOrientation!";
}
}
}
......@@ -22,8 +22,10 @@
#include "hacks.h"
NemoWindow::NemoWindow(QWindow *parent) :
QQuickWindow(parent)
QQuickWindow(parent),
m_defaultAllowedOrientations(Qt::PortraitOrientation | Qt::LandscapeOrientation)
{
m_allowedOrientations = m_defaultAllowedOrientations;
}
Qt::ScreenOrientations NemoWindow::allowedOrientations() const
......@@ -31,11 +33,20 @@ Qt::ScreenOrientations NemoWindow::allowedOrientations() const
return m_allowedOrientations;
}
const Qt::ScreenOrientations NemoWindow::defaultAllowedOrientations() const
{
return m_defaultAllowedOrientations;
}
void NemoWindow::setAllowedOrientations(Qt::ScreenOrientations allowed)
{
//This way no invalid values can get assigned to allowedOrientations
if (m_allowedOrientations != allowed && Hacks::isOrientationMaskValid(allowed)) {
m_allowedOrientations = allowed;
emit allowedOrientationsChanged();
if (m_allowedOrientations != allowed) {
if (Hacks::isOrientationMaskValid(allowed)) {
m_allowedOrientations = allowed;
emit allowedOrientationsChanged();
} else {
qDebug() << "NemoWindow: invalid allowedOrientation!";
}
}
}
......@@ -26,10 +26,14 @@ class NemoWindow : public QQuickWindow
{
Q_OBJECT
Q_PROPERTY(Qt::ScreenOrientations allowedOrientations READ allowedOrientations WRITE setAllowedOrientations NOTIFY allowedOrientationsChanged)
Q_PROPERTY(Qt::ScreenOrientations defaultAllowedOrientations READ allowedOrientations)
public:
explicit NemoWindow(QWindow *parent = 0);
Qt::ScreenOrientations allowedOrientations() const;
const Qt::ScreenOrientations defaultAllowedOrientations() const;
void setAllowedOrientations(Qt::ScreenOrientations allowed);
signals:
......@@ -38,7 +42,12 @@ signals:
public slots:
private:
//This is the global allowed orientations settings:
//it's the settings used when the current Page doesn't specify any allowedOrientations, otherwise
//allowedOrientations of that Page is used
Qt::ScreenOrientations m_allowedOrientations;
Qt::ScreenOrientations m_defaultAllowedOrientations;
};
#endif // NEMOWINDOW_H
......@@ -16,6 +16,9 @@ Label 1.0 Label.qml
CheckBox 1.0 Checkbox.qml
ButtonRow 1.0 ButtonRow.qml
QueryDialog 1.0 QueryDialog.qml
Header 1.0 Header.qml
HeaderToolsLayout 1.0 HeaderToolsLayout.qml
# MIRRORED CONTROLS:
# These are the controls that we take directly from official QQC.
# This is to avoid having to "import QtQuick.Controls" when using controls
......
#include "qquickfilteringmousearea.h"
#include <QQuickWindow>
QQuickFilteringMouseArea::QQuickFilteringMouseArea(QQuickItem *parent) :
QQuickItem(parent),
m_pressed(false),
m_swipingX(false),
m_swipingY(false),
m_swipingThreshold(10)
{
setFiltersChildMouseEvents(true);
setAcceptedMouseButtons(Qt::LeftButton);
}
bool QQuickFilteringMouseArea::childMouseEventFilter(QQuickItem *i, QEvent *e)
{
if (!isVisible() || !isEnabled())
return QQuickItem::childMouseEventFilter(i, e);
switch (e->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
return sendMouseEvent(i, static_cast<QMouseEvent *>(e));
case QEvent::UngrabMouse:
if (window() && window()->mouseGrabberItem() && window()->mouseGrabberItem() != this) {
// The grab has been taken away from a child and given to some other item.
mouseUngrabEvent();
}
break;
default:
break;
}
return QQuickItem::childMouseEventFilter(i, e);
}
void QQuickFilteringMouseArea::mouseMoveEvent(QMouseEvent *event) {
if (!isEnabled() || !isPressed()) {
QQuickItem::mouseMoveEvent(event);
return;
}
//TODO: we should only grab the mouse if there's a swipe ongoing.
//The move event is very easy to trigger with touchscreens
//so it's not a good measure of the fact that we want to swipe
//grabMouse();
setDeltaPos(QPointF(event->windowPos().x() - pressPos().x(), event->windowPos().y() - pressPos().y()));
if (event->windowPos().x() - pressPos().x() > swipingThreshold()) setSwipingX(true);
if (event->windowPos().y() - pressPos().y() > swipingThreshold()) setSwipingY(true);
setPosition(event->localPos());
}
void QQuickFilteringMouseArea::mousePressEvent(QMouseEvent *event) {
if (!isEnabled() || !(event->button() & acceptedMouseButtons())) {
QQuickItem::mousePressEvent(event);
} else {
setPressPos(event->windowPos());
emit pressed(event->localPos());
setPressed(true);
setPosition(event->localPos());
}
}
void QQuickFilteringMouseArea::mouseReleaseEvent(QMouseEvent *event) {
if (!isEnabled() && !isPressed()) {
QQuickItem::mouseReleaseEvent(event);
} else {
QQuickWindow *w = window();
if (w && w->mouseGrabberItem() == this && m_pressed){
emit released(event->localPos());
mouseUngrabEvent();
}
}
}
bool QQuickFilteringMouseArea::sendMouseEvent(QQuickItem *item, QMouseEvent *event) {
QPointF localPos = mapFromScene(event->windowPos());
QQuickWindow *c = window();
QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
if ((contains(localPos)) && (!grabber || !grabber->keepMouseGrab())) {
QMouseEvent mouseEvent(event->type(), localPos, event->windowPos(), event->screenPos(),
event->button(), event->buttons(), event->modifiers());
mouseEvent.setAccepted(false);
switch (event->type()) {
case QEvent::MouseMove:
mouseMoveEvent(&mouseEvent);
break;
case QEvent::MouseButtonPress:
mousePressEvent(&mouseEvent);
break;
case QEvent::MouseButtonRelease:
mouseReleaseEvent(&mouseEvent);
break;
default:
break;
}
}
return false;
}
void QQuickFilteringMouseArea::mouseUngrabEvent() {
setPressed(false);
setSwipingX(false);
setSwipingY(false);
QQuickWindow *w = window();
if (w && w->mouseGrabberItem() == this)
ungrabMouse();
}
void QQuickFilteringMouseArea::grabMouseEvents() {
qDebug() << "Glacier Header: Grabbing mouse!";
grabMouse();
}
void QQuickFilteringMouseArea::ungrabMouseEvents() {
ungrabMouse();
}
#ifndef QQUICKFILTERINGMOUSEAREA_H
#define QQUICKFILTERINGMOUSEAREA_H
#include <QQuickItem>
#include <QMouseEvent>
class QQuickFilteringMouseArea : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
Q_PROPERTY(QPointF pressPos READ pressPos NOTIFY pressPosChanged)
Q_PROPERTY(QPointF deltaPos READ deltaPos NOTIFY deltaPosChanged)
Q_PROPERTY(bool swipingX READ isSwipingX NOTIFY swipingXChanged)
Q_PROPERTY(bool swipingY READ isSwipingY NOTIFY swipingYChanged)
Q_PROPERTY(int swipingThreshold READ swipingThreshold WRITE setSwipingThreshold NOTIFY swipingThresholdChanged)
public:
explicit QQuickFilteringMouseArea(QQuickItem *parent = 0);
bool handlePress();
bool handleMove();
bool handleRelease();
bool isPressed() const { return m_pressed; }
void setPressed(const bool pressed) {
if (m_pressed != pressed) {
m_pressed = pressed;
emit pressedChanged();
}
}
QPointF position() const { return m_lastPos; }
void setPosition(const QPointF &pos) {
if (m_lastPos != pos) {
m_lastPos = pos;
emit positionChanged(pos);
}
}
QPointF pressPos() const { return m_pressPos; }
void setPressPos(const QPointF &pos) {
if (m_pressPos != pos) {
m_pressPos = pos;
emit pressPosChanged();
}
}
QPointF deltaPos() const { return m_deltaPos; }
void setDeltaPos(const QPointF &pos) {
if (m_deltaPos != pos) {
m_deltaPos = pos;
emit deltaPosChanged();
}
}
bool isSwipingX() const { return m_swipingX; }
void setSwipingX(const bool swiping) {
if (m_swipingX != swiping) {
m_swipingX = swiping;
emit swipingXChanged();
}
}
bool isSwipingY() const { return m_swipingY; }
void setSwipingY(const bool swiping) {
if (m_swipingY != swiping) {
m_swipingY = swiping;
emit swipingYChanged();
}
}
int swipingThreshold() const { return m_swipingThreshold; }
void setSwipingThreshold(const int threshold) {
if (m_swipingThreshold != threshold) {
m_swipingThreshold = threshold;
emit swipingThresholdChanged();
}
}
signals:
void pressedChanged();
void pressed(const QPointF &pos);
void released(const QPointF &pos);
void positionChanged(const QPointF &pos);
void pressPosChanged();
void deltaPosChanged();
void swipingXChanged();
void swipingYChanged();
void swipingThresholdChanged();
public slots:
void grabMouseEvents();
void ungrabMouseEvents();
protected:
virtual bool childMouseEventFilter(QQuickItem *, QEvent *);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void mouseUngrabEvent();
bool sendMouseEvent(QQuickItem *item, QMouseEvent *event);
private:
bool m_pressed;
QPointF m_lastPos;
QPointF m_pressPos;
QPointF m_deltaPos;
bool m_swipingX;
bool m_swipingY;
int m_swipingThreshold;
};
#endif // QQUICKFILTERINGMOUSEAREA_H
......@@ -23,6 +23,7 @@
#include "hacks.h"
#include "nemowindow.h"
#include "nemopage.h"
#include "qquickfilteringmousearea.h"
QQuickNemoControlsExtensionPlugin::QQuickNemoControlsExtensionPlugin(QObject *parent) :
QQmlExtensionPlugin(parent)
......@@ -41,6 +42,7 @@ void QQuickNemoControlsExtensionPlugin::registerTypes(const char *uri)
qmlRegisterSingletonType<QObject>(uri, 1, 0, "NemoHacks", nemo_hacks_singletontype_provider);
qmlRegisterType<NemoWindow>(uri, 1, 0, "NemoWindow");
qmlRegisterType<NemoPage>(uri, 1, 0, "NemoPage");
qmlRegisterType<QQuickFilteringMouseArea>(uri, 1, 0, "FilteringMouseArea");
}
void QQuickNemoControlsExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
......
......@@ -46,6 +46,7 @@ Style {
Image {
id: icon
anchors.fill: parent
fillMode: Image.PreserveAspectFit
anchors.margins: 8
source: control.iconSource
}
......
......@@ -50,6 +50,7 @@ NemoTheme::NemoTheme(QObject *parent)
, m_label(new NemoThemeLabel(this))
, m_checkbox(new NemoThemeCheckbox(this))
, m_buttonRow(new NemoThemeButtonRow(this))
, m_header(new NemoThemeHeader(this))
{
loadFromFile(GLACIER_THEME);
int id = QFontDatabase::addApplicationFont("/usr/share/fonts/google-opensans/OpenSans-Regular.ttf");
......@@ -147,6 +148,11 @@ NemoThemeButtonRow * NemoTheme::buttonRow() const
return m_buttonRow;
}
NemoThemeHeader * NemoTheme::header() const
{
return m_header;
}
QString NemoTheme::fontFamily() const
{
return m_fontFamily;
......@@ -389,25 +395,6 @@ void NemoTheme::loadFromFile(const QString &fileName)
// Setting properties for page
QJsonObject stylesPage = styles.value("page").toObject();
m_page->setBackground(jsonToColor(jsonValue(stylesPage, "background", "page"), defines));
// Setting properties for dimmer
QJsonObject stylesPageDimmer = stylesPage.value("dimmer").toObject();
m_page->dimmer()->setStartColor(jsonToColor(jsonValue(stylesPageDimmer, "startColor", "dimmer"), defines));
m_page->dimmer()->setEndColor(jsonToColor(jsonValue(stylesPageDimmer, "endColor", "dimmer"), defines));
if (stylesPageDimmer.contains("height")) {
m_page->dimmer()->setHeight(jsonToInt(stylesPageDimmer.value("height"), defines));
} else {
m_page->dimmer()->setHeightDefault();
}
if (stylesPageDimmer.contains("startPosition")) {
m_page->dimmer()->setStartPosition(jsonToDouble(stylesPageDimmer.value("startPosition"), defines));
} else {
m_page->dimmer()->setStartPositionDefault();
}
if (stylesPageDimmer.contains("endPosition")) {
m_page->dimmer()->setEndPosition(jsonToDouble(stylesPageDimmer.value("endPosition"), defines));
} else {
m_page->dimmer()->setEndPositionDefault();
}
// Setting properties for pageStack
QJsonObject stylesPageStack = styles.value("pageStack").toObject();
if (stylesPageStack.contains("transitionDuration")) {
......@@ -460,4 +447,26 @@ void NemoTheme::loadFromFile(const QString &fileName)
QJsonObject stylesButtonRow = styles.value("buttonRow").toObject();
m_buttonRow->setBackground(jsonToColor(jsonValue(stylesButtonRow, "background", "buttonRow"), defines));
m_buttonRow->setButtonColor(jsonToColor(jsonValue(stylesButtonRow, "buttonColor", "buttonRow"), defines));
// Setting properties for header
QJsonObject stylesHeader = styles.value("header").toObject();
m_header->setBackground(jsonToColor(jsonValue(stylesHeader, "background", "header"), defines));
// Setting properties for dimmer
QJsonObject stylesHeaderDimmer = stylesHeader.value("dimmer").toObject();
m_header->dimmer()->setStartColor(jsonToColor(jsonValue(stylesHeaderDimmer, "startColor", "dimmer"), defines));
m_header->dimmer()->setEndColor(jsonToColor(jsonValue(stylesHeaderDimmer, "endColor", "dimmer"), defines));
if (stylesHeaderDimmer.contains("height")) {
m_header->dimmer()->setHeight(jsonToInt(stylesHeaderDimmer.value("height"), defines));
} else {
m_header->dimmer()->setHeightDefault();
}
if (stylesHeaderDimmer.contains("startPosition")) {
m_header->dimmer()->setStartPosition(jsonToDouble(stylesHeaderDimmer.value("startPosition"), defines));
} else {
m_header->dimmer()->setStartPositionDefault();
}
if (stylesHeaderDimmer.contains("endPosition")) {
m_header->dimmer()->setEndPosition(jsonToDouble(stylesHeaderDimmer.value("endPosition"), defines));
} else {
m_header->dimmer()->setEndPositionDefault();
}
}
......@@ -36,6 +36,7 @@
#include "nemothemelabel.h"
#include "nemothemecheckbox.h"
#include "nemothemebuttonrow.h"
#include "nemothemeheader.h"
class NemoTheme: public QObject
{
......@@ -54,6 +55,7 @@ class NemoTheme: public QObject
Q_PROPERTY(NemoThemeLabel * label READ label CONSTANT)
Q_PROPERTY(NemoThemeCheckbox * checkbox READ checkbox CONSTANT)
Q_PROPERTY(NemoThemeButtonRow * buttonRow READ buttonRow CONSTANT)
Q_PROPERTY(NemoThemeHeader * header READ header CONSTANT)
Q_PROPERTY(QString fontFamily READ fontFamily CONSTANT)
public:
explicit NemoTheme(QObject *parent = 0);
......@@ -73,6 +75,7 @@ public:
NemoThemeLabel * label() const;
NemoThemeCheckbox * checkbox() const;
NemoThemeButtonRow * buttonRow() const;
NemoThemeHeader * header() const;
QString fontFamily() const;
public Q_SLOTS:
void loadFromFile(const QString &fileName);
......@@ -94,6 +97,7 @@ private:
NemoThemeLabel * m_label;
NemoThemeCheckbox * m_checkbox;
NemoThemeButtonRow * m_buttonRow;
NemoThemeHeader * m_header;
QString m_fontFamily;
};
......
/*
* Copyright (C) 2013 Lucien Xu <sfietkonstantin@free.fr>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
// This class is autogenerated using themehelper.py
// Any modification done in this file will be overridden
#include "nemothemeheader.h"
NemoThemeHeader::NemoThemeHeader(QObject *parent)
: QObject(parent)
, m_dimmer(new NemoThemeHeaderDimmer(this))
{
}
QColor NemoThemeHeader::background() const
{
return m_background;
}
void NemoThemeHeader::setBackground(const QColor &background)
{
if (m_background != background) {
m_background = background;
emit backgroundChanged();
}
}
NemoThemeHeaderDimmer * NemoThemeHeader::dimmer() const
{
return m_dimmer;
}
/*
* Copyright (C) 2013 Lucien Xu <sfietkonstantin@free.fr>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
// This class is autogenerated using themehelper.py
// Any modification done in this file will be overridden
#ifndef NEMOTHEMEHEADER_H
#define NEMOTHEMEHEADER_H
#include <QtCore/QObject>
#include <QtGui/QColor>
#include "nemothemeheaderdimmer.h"
class NemoThemeHeader: public QObject
{
Q_OBJECT
Q_PROPERTY(QColor background READ background NOTIFY backgroundChanged)
Q_PROPERTY(NemoThemeHeaderDimmer * dimmer READ dimmer CONSTANT)
public:
explicit NemoThemeHeader(QObject *parent = 0);
QColor background() const;
void setBackground(const QColor &background);
NemoThemeHeaderDimmer * dimmer() const;
Q_SIGNALS:
void backgroundChanged();
private:
QColor m_background;
NemoThemeHeaderDimmer * m_dimmer;
};
#endif //NEMOTHEMEHEADER_H
/*
* Copyright (C) 2013 Lucien Xu <sfietkonstantin@free.fr>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
// This class is autogenerated using themehelper.py
// Any modification done in this file will be overridden
#include "nemothemeheaderdimmer.h"
NemoThemeHeaderDimmer::NemoThemeHeaderDimmer(QObject *parent)
: QObject(parent)
, m_height(16)
, m_startPosition(0)
, m_endPosition(1.0)
{
}
QColor NemoThemeHeaderDimmer::startColor() const
{
return m_startColor;
}
void NemoThemeHeaderDimmer::setStartColor(const QColor &startColor)
{
if (m_startColor != startColor) {
m_startColor = startColor;
emit startColorChanged();
}
}
QColor NemoThemeHeaderDimmer::endColor() const
{
return m_endColor;
}
void NemoThemeHeaderDimmer::setEndColor(const QColor &endColor)
{
if (m_endColor != endColor) {
m_endColor = endColor;
emit endColorChanged();
}
}
int NemoThemeHeaderDimmer::height() const
{
return m_height;
}
void NemoThemeHeaderDimmer::setHeight(int height)
{
if (m_height != height) {
m_height = height;
emit heightChanged();
}
}
void NemoThemeHeaderDimmer::setHeightDefault()
{
if (m_height != 16) {
m_height = 16;
emit heightChanged();
}
}
double NemoThemeHeaderDimmer::startPosition() const
{
return m_startPosition;
}
void NemoThemeHeaderDimmer::setStartPosition(double startPosition)
{
if (m_startPosition != startPosition) {
m_startPosition = startPosition;
emit startPositionChanged();
}
}
void NemoThemeHeaderDimmer::setStartPositionDefault()
{
if (m_startPosition != 0) {
m_startPosition = 0;
emit startPositionChanged();
}
}
double NemoThemeHeaderDimmer::endPosition() const
{
return m_endPosition;
}
void NemoThemeHeaderDimmer::setEndPosition(double endPosition)
{
if (m_endPosition != endPosition) {
m_endPosition = endPosition;
emit endPositionChanged();
}
}
void NemoThemeHeaderDimmer::setEndPositionDefault()
{
if (m_endPosition != 1.0) {
m_endPosition = 1.0;
emit endPositionChanged();
}
}
/*
* Copyright (C) 2013 Lucien Xu <sfietkonstantin@free.fr>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
// This class is autogenerated using themehelper.py
// Any modification done in this file will be overridden
#ifndef NEMOTHEMEHEADERDIMMER_H
#define NEMOTHEMEHEADERDIMMER_H
#include <QtCore/QObject>
#include <QtGui/QColor>
class NemoThemeHeaderDimmer: public QObject
{
Q_OBJECT
Q_PROPERTY(QColor startColor READ startColor NOTIFY startColorChanged)
Q_PROPERTY(QColor endColor READ endColor NOTIFY endColorChanged)
Q_PROPERTY(int height READ height NOTIFY heightChanged)
Q_PROPERTY(double startPosition READ startPosition NOTIFY startPositionChanged)
Q_PROPERTY(double endPosition READ endPosition NOTIFY endPositionChanged)
public:
explicit NemoThemeHeaderDimmer(QObject *parent = 0);
QColor startColor() const;
void setStartColor(const QColor &startColor);
QColor endColor() const;
void setEndColor(const QColor &endColor);
int height() const;
void setHeight(int height);
void setHeightDefault();
double startPosition() const;
void setStartPosition(double startPosition);
void setStartPositionDefault();
double endPosition() const;
void setEndPosition(double endPosition);
void setEndPositionDefault();
Q_SIGNALS:
void startColorChanged();
void endColorChanged();
void heightChanged();
void startPositionChanged();
void endPositionChanged();
private:
QColor m_startColor;
QColor m_endColor;
int m_height;
double m_startPosition;
double m_endPosition;
};
#endif //NEMOTHEMEHEADERDIMMER_H
......@@ -24,7 +24,6 @@
NemoThemePage::NemoThemePage(QObject *parent)
: QObject(parent)
, m_dimmer(new NemoThemePageDimmer(this))
{
}
......@@ -40,8 +39,3 @@ void NemoThemePage::setBackground(const QColor &background)
emit backgroundChanged();
}
}
NemoThemePageDimmer * NemoThemePage::dimmer() const
{
return m_dimmer;
}
......@@ -25,23 +25,19 @@
#include <QtCore/QObject>
#include <QtGui/QColor>
#include "nemothemepagedimmer.h"
class NemoThemePage: public QObject
{
Q_OBJECT
Q_PROPERTY(QColor background READ background NOTIFY backgroundChanged)
Q_PROPERTY(NemoThemePageDimmer * dimmer READ dimmer CONSTANT)
public:
explicit NemoThemePage(QObject *parent = 0);
QColor background() const;
void setBackground(const QColor &background);
NemoThemePageDimmer * dimmer() const;
Q_SIGNALS:
void backgroundChanged();
private:
QColor m_background;
NemoThemePageDimmer * m_dimmer;
};
#endif //NEMOTHEMEPAGE_H
......@@ -24,7 +24,7 @@
NemoThemePageDimmer::NemoThemePageDimmer(QObject *parent)
: QObject(parent)
, m_height(15)
, m_height(16)
, m_startPosition(0)
, m_endPosition(1.0)
{
......@@ -71,8 +71,8 @@ void NemoThemePageDimmer::setHeight(int height)
void NemoThemePageDimmer::setHeightDefault()
{
if (m_height != 15) {
m_height = 15;
if (m_height != 16) {
m_height = 16;
emit heightChanged();
}
}
......
......@@ -48,12 +48,12 @@ void QQuickNemoStyleExtensionPlugin::registerTypes(const char *uri)
qmlRegisterUncreatableType<NemoThemeToolBar>(uri, 1, 0, "NemoThemeToolBar", reason);
qmlRegisterUncreatableType<NemoThemeWindow>(uri, 1, 0, "NemoThemeWindow", reason);
qmlRegisterUncreatableType<NemoThemePage>(uri, 1, 0, "NemoThemePage", reason);
qmlRegisterUncreatableType<NemoThemePageDimmer>(uri, 1, 0, "NemoThemePageDimmer", reason);
qmlRegisterUncreatableType<NemoThemeSpinner>(uri, 1, 0, "NemoThemeSpinner", reason);
qmlRegisterUncreatableType<NemoThemeLabel>(uri, 1, 0, "NemoThemeLabel", reason);
qmlRegisterUncreatableType<NemoThemeCheckbox>(uri, 1, 0, "NemoThemeCheckbox", reason);
qmlRegisterUncreatableType<NemoThemePageStack>(uri, 1, 0, "NemoThemePageStack", reason);
qmlRegisterUncreatableType<NemoThemeHeader>(uri, 1, 0, "NemoThemeHeader", reason);
qmlRegisterUncreatableType<NemoThemeHeaderDimmer>(uri, 1, 0, "NemoThemeHeaderDimmer", reason);
qmlRegisterSingletonType<QObject>(uri, 1, 0, "Theme", nemo_theme_provider);
}
......
......@@ -84,12 +84,13 @@ HEADERS += \
autogenerated/nemothemetoolbar.h \
autogenerated/nemothemewindow.h \
autogenerated/nemothemepage.h \
autogenerated/nemothemepagedimmer.h \
autogenerated/nemothemespinner.h \
autogenerated/nemothemelabel.h \
autogenerated/nemothemecheckbox.h \
autogenerated/nemothemepagestack.h \
autogenerated/nemothemebuttonrow.h
autogenerated/nemothemebuttonrow.h \
autogenerated/nemothemeheader.h \
autogenerated/nemothemeheaderdimmer.h
SOURCES += \
qquicknemostyleextensionplugin.cpp \
......@@ -103,12 +104,13 @@ SOURCES += \
autogenerated/nemothemetoolbar.cpp \
autogenerated/nemothemewindow.cpp \
autogenerated/nemothemepage.cpp \
autogenerated/nemothemepagedimmer.cpp \
autogenerated/nemothemespinner.cpp \
autogenerated/nemothemelabel.cpp \
autogenerated/nemothemecheckbox.cpp \
autogenerated/nemothemepagestack.cpp \
autogenerated/nemothemebuttonrow.cpp
autogenerated/nemothemebuttonrow.cpp \
autogenerated/nemothemeheader.cpp \
autogenerated/nemothemeheaderdimmer.cpp
INSTALLS += target images qmlfiles themes
......
......@@ -45,11 +45,7 @@
"background": "#000000"
},
"page": {
"background": "#000000",
"dimmer": {
"startColor": "black",
"endColor": "transparent"
}
"background": "#000000"
},
"spinner": {
"primaryColor": "#ffffff",
......@@ -65,6 +61,13 @@
"buttonRow": {
"background": "#313131",
"buttonColor": "#0091e5"
},
"header": {
"background": "#000000",
"dimmer": {
"startColor": "black",
"endColor": "transparent"
}
}
}
}
......@@ -45,11 +45,7 @@
"background": "#000000"
},
"page": {
"background": "#000000",
"dimmer": {
"startColor": "black",
"endColor": "transparent"
}
"background": "#000000"
},
"spinner": {
"primaryColor": "accentColor",
......@@ -64,6 +60,13 @@
},
"pageStack": {
"transitionDuration": 1500
},
"header": {
"background": "#000000",
"dimmer": {
"startColor": "black",
"endColor": "transparent"
}
}
}
}
......@@ -73,10 +73,6 @@
{
"name": "background",
"type": "QColor"
},
{
"name": "dimmer",
"object": "PageDimmer"
}
]
},
......@@ -141,34 +137,6 @@
}
]
},
{
"name": "PageDimmer",
"properties": [
{
"name": "startColor",
"type": "QColor"
},
{
"name": "endColor",
"type": "QColor"
},
{
"name": "height",
"type": "int",
"default": 15
},
{
"name": "startPosition",
"type": "double",
"default": 0
},
{
"name": "endPosition",
"type": "double",
"default": 1.0
}
]
},
{
"name": "Spinner",
"properties": [
......@@ -256,6 +224,47 @@
"type": "QColor"
}
]
},
{
"name": "Header",
"properties": [
{
"name": "background",
"type": "QColor"
},
{
"name": "dimmer",
"object": "HeaderDimmer"
}
]
},
{
"name": "HeaderDimmer",
"properties": [
{
"name": "startColor",
"type": "QColor"
},
{
"name": "endColor",
"type": "QColor"
},
{
"name": "height",
"type": "int",
"default": 16
},
{
"name": "startPosition",
"type": "double",
"default": 0
},
{
"name": "endPosition",
"type": "double",
"default": 1.0
}
]
}
],
"properties": [
......@@ -306,6 +315,10 @@
{
"name": "buttonRow",
"object": "ButtonRow"
},
{
"name": "header",
"object": "Header"
}
],
"font": "/usr/share/fonts/google-opensans/OpenSans-Regular.ttf"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment