initial port of ToolButton
wonder if will be possible to share something with Button, but ToolButton is way more complicated
This commit is contained in:
parent
a059748fbc
commit
a3266857ad
@ -19,427 +19,23 @@
|
||||
*/
|
||||
|
||||
import QtQuick 2.1
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
import QtQuick.Controls.Private 1.0
|
||||
import QtQuick.Controls 1.0
|
||||
import "private" as Private
|
||||
import QtQuick.Controls 1.2 as QtControls
|
||||
import "styles" as Styles
|
||||
|
||||
/**
|
||||
* A plasma theme based toolbutton.
|
||||
*/
|
||||
Item {
|
||||
Button {
|
||||
id: button
|
||||
|
||||
// Commmon API
|
||||
/**
|
||||
* true if the button is flat.
|
||||
*/
|
||||
property bool flat: true
|
||||
|
||||
/**
|
||||
* true if the button is checked.
|
||||
*/
|
||||
property bool checked: defaultAction ? defaultAction.checked : false
|
||||
|
||||
/**
|
||||
* true if the button is checkable.
|
||||
*/
|
||||
property bool checkable: defaultAction ? defaultAction.checkable : false
|
||||
|
||||
/**
|
||||
* type:string
|
||||
* true if the button is currently pressed.
|
||||
*/
|
||||
property alias pressed: mouse.pressed
|
||||
|
||||
/**
|
||||
* type:string
|
||||
* The text of the button
|
||||
*/
|
||||
property alias text: label.text
|
||||
|
||||
/*! This property holds the button tooltip. */
|
||||
property string tooltip
|
||||
|
||||
/**
|
||||
* type:variant
|
||||
*
|
||||
* Sets the icon for the button. It can be any image from any protocol
|
||||
* supported by the Image element, or a freedesktop-compatible icon name
|
||||
*/
|
||||
property alias iconSource: icon.source
|
||||
|
||||
/**
|
||||
* type:font
|
||||
* The font for the button
|
||||
*/
|
||||
property alias font: label.font
|
||||
|
||||
/**
|
||||
* Emited when the button is clicked.
|
||||
*/
|
||||
signal clicked()
|
||||
|
||||
// Plasma extensiuons
|
||||
property QtObject defaultAction
|
||||
|
||||
|
||||
property bool flat: true
|
||||
|
||||
enabled: defaultAction == undefined || defaultAction.enabled
|
||||
|
||||
/**
|
||||
* The smallest width this button can be to show all the contents
|
||||
*/
|
||||
//icon + label + left margin + right margin + spacing between icon and text
|
||||
//here it assumesleft margin = right top = bottom, why?
|
||||
// because the right and bottom margins can be disabled, so they would return 0, but their actual size is still needed for size hints
|
||||
property real minimumWidth: Math.max(units.iconSizes.small, height) + label.implicitWidth + (delegate.margins ? (delegate.margins.left + delegate.margins.left + ((icon.valid) ? delegate.margins.left : 0)) : 0)
|
||||
|
||||
/**
|
||||
* The smallest height this button can be to show all the contents
|
||||
*/
|
||||
property real minimumHeight: Math.max(units.iconSizes.small, label.implicitHeight) + (delegate.margins ? (delegate.margins.top + delegate.margins.top) : 0)
|
||||
|
||||
LayoutMirroring.enabled: (Qt.application.layoutDirection === Qt.RightToLeft)
|
||||
LayoutMirroring.childrenInherit: true
|
||||
implicitWidth: {
|
||||
if (label.text.length == 0) {
|
||||
height;
|
||||
} else {
|
||||
Math.max(theme.mSize(theme.defaultFont).width*12, minimumWidth);
|
||||
}
|
||||
}
|
||||
|
||||
implicitHeight: Math.max(theme.mSize(theme.defaultFont).height*1.6, minimumHeight)
|
||||
|
||||
// TODO: needs to define if there will be specific graphics for
|
||||
// disabled buttons
|
||||
opacity: enabled ? 1.0 : 0.5
|
||||
|
||||
Keys.onSpacePressed: internal.userPressed = true
|
||||
Keys.onReturnPressed: internal.userPressed = true
|
||||
Keys.onReleased: {
|
||||
internal.userPressed = false
|
||||
if (event.key == Qt.Key_Space ||
|
||||
event.key == Qt.Key_Return)
|
||||
internal.clickButton()
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
shadow.state = "focus"
|
||||
} else if (checked) {
|
||||
shadow.state = "hidden"
|
||||
} else {
|
||||
shadow.state = "shadow"
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: internal
|
||||
property bool userPressed: false
|
||||
|
||||
function clickButton()
|
||||
{
|
||||
if (!button.enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (defaultAction && defaultAction.checkable) {
|
||||
defaultAction.checked = !defaultAction.checked
|
||||
} else if (button.checkable) {
|
||||
button.checked = !button.checked
|
||||
}
|
||||
|
||||
if (button.KeyNavigation.tab || button.KeyNavigation.backtab) {
|
||||
// Only focus the button if it is set up for keyboard
|
||||
// navigation. This avoid getting a strange focus frame around
|
||||
// buttons which are usually not focusable, such as buttons in
|
||||
// a toolbar.
|
||||
button.forceActiveFocus();
|
||||
}
|
||||
button.clicked()
|
||||
|
||||
if (defaultAction) {
|
||||
defaultAction.trigger()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: delegate
|
||||
anchors.fill: parent
|
||||
property QtObject margins: item.margins
|
||||
property string shadowState: "shadow"
|
||||
sourceComponent: {
|
||||
if (label.text.length == 0 && button.width == button.height && (button.parent && button.parent.checkedButton === undefined) && !flat) {
|
||||
return roundButtonComponent
|
||||
} else {
|
||||
return buttonComponent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: buttonComponent
|
||||
Item {
|
||||
parent: delegate
|
||||
anchors.fill: parent
|
||||
property alias margins: surface.fixedMargins
|
||||
property alias hasOverState: shadow.hasOverState
|
||||
Private.ButtonShadow {
|
||||
id: shadow
|
||||
anchors.fill: parent
|
||||
visible: !flat && (surface.enabledBorders == "AllBorders" || state == "hover" || state == "focus")
|
||||
state: delegate.shadowState
|
||||
}
|
||||
|
||||
PlasmaCore.FrameSvgItem {
|
||||
id: surface
|
||||
|
||||
enabledBorders: {
|
||||
if (flat || !button.parent ||
|
||||
button.parent.width < button.parent.implicitWidth ||
|
||||
button.parent.checkedButton === undefined ||
|
||||
!bordersSvg.hasElement("pressed-hint-compose-over-border")) {
|
||||
if (shadows !== null) {
|
||||
shadows.destroy()
|
||||
}
|
||||
return "AllBorders"
|
||||
}
|
||||
|
||||
var borders = new Array()
|
||||
if (button.x == 0) {
|
||||
borders.push("LeftBorder")
|
||||
shadow.anchors.leftMargin = 0;
|
||||
} else {
|
||||
shadow.anchors.leftMargin = -1;
|
||||
}
|
||||
if (button.y == 0) {
|
||||
borders.push("TopBorder")
|
||||
shadow.anchors.topMargin = 0;
|
||||
} else {
|
||||
shadow.anchors.topMargin = -1;
|
||||
}
|
||||
if (button.x + button.width >= button.parent.width) {
|
||||
borders.push("RightBorder")
|
||||
shadow.anchors.rightMargin = 0;
|
||||
} else {
|
||||
shadow.anchors.rightMargin = -1;
|
||||
}
|
||||
if (button.y + button.height >= button.parent.height) {
|
||||
borders.push("BottomBorder")
|
||||
shadow.anchors.bottomMargin = 0;
|
||||
} else {
|
||||
shadow.anchors.bottomMargin = -1;
|
||||
}
|
||||
|
||||
if (shadows === null) {
|
||||
shadows = shadowsComponent.createObject(surface)
|
||||
}
|
||||
|
||||
return borders.join("|")
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
imagePath: "widgets/button"
|
||||
prefix: (internal.userPressed || checked) ? "pressed" : "normal"
|
||||
//internal: if there is no hover status, don't paint on mouse over in touchscreens
|
||||
opacity: (internal.userPressed || checked || !flat || (shadow.hasOverState && mouse.containsMouse && button.enabled)) ? 1 : 0
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: units.longDuration }
|
||||
}
|
||||
|
||||
PlasmaCore.Svg {
|
||||
id: bordersSvg
|
||||
imagePath: "widgets/button"
|
||||
}
|
||||
|
||||
property Item shadows
|
||||
Component {
|
||||
id: shadowsComponent
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
PlasmaCore.SvgItem {
|
||||
svg: bordersSvg
|
||||
width: naturalSize.width
|
||||
elementId: surface.prefix+"-left"
|
||||
visible: button.x > 0
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
margins: 1
|
||||
leftMargin: -1
|
||||
}
|
||||
}
|
||||
PlasmaCore.SvgItem {
|
||||
svg: bordersSvg
|
||||
width: naturalSize.width
|
||||
elementId: surface.prefix+"-right"
|
||||
visible: button.x + button.width < button.parent.width
|
||||
anchors {
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
margins: 1
|
||||
rightMargin: -1
|
||||
}
|
||||
}
|
||||
PlasmaCore.SvgItem {
|
||||
svg: bordersSvg
|
||||
height: naturalSize.height
|
||||
elementId: surface.prefix+"-top"
|
||||
visible: button.y > 0
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
margins: 1
|
||||
topMargin: -1
|
||||
}
|
||||
}
|
||||
PlasmaCore.SvgItem {
|
||||
svg: bordersSvg
|
||||
height: naturalSize.height
|
||||
elementId: surface.prefix+"-bottom"
|
||||
visible: button.y + button.height < button.parent.height
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
margins: 1
|
||||
bottomMargin: -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roundButtonComponent
|
||||
Item {
|
||||
id: roundButtonDelegate
|
||||
parent: delegate
|
||||
anchors.fill: parent
|
||||
property QtObject margins: QtObject {
|
||||
property int left: delegate.width/8
|
||||
property int top: delegate.width/8
|
||||
property int right: delegate.width/8
|
||||
property int bottom: delegate.width/8
|
||||
}
|
||||
property alias hasOverState: roundShadow.hasOverState
|
||||
Private.RoundShadow {
|
||||
id: roundShadow
|
||||
visible: !flat
|
||||
anchors.fill: parent
|
||||
state: delegate.shadowState
|
||||
}
|
||||
|
||||
PlasmaCore.Svg {
|
||||
id: buttonSvg
|
||||
imagePath: "widgets/actionbutton"
|
||||
}
|
||||
|
||||
PlasmaCore.SvgItem {
|
||||
id: buttonItem
|
||||
svg: buttonSvg
|
||||
elementId: (internal.userPressed || checked) ? "pressed" : "normal"
|
||||
width: Math.floor(parent.height/2) * 2
|
||||
height: width
|
||||
//internal: if there is no hover status, don't paint on mouse over in touchscreens
|
||||
opacity: (internal.userPressed || checked || !flat || (roundShadow.hasOverState && mouse.containsMouse)) ? 1 : 0
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: units.longDuration }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: delegate.margins ? delegate.margins.left : 0
|
||||
topMargin: delegate.margins ? delegate.margins.top : 0
|
||||
rightMargin: delegate.margins ? delegate.margins.right : 0
|
||||
bottomMargin: delegate.margins ? delegate.margins.bottom : 0
|
||||
}
|
||||
|
||||
spacing: icon.valid && delegate.margins ? delegate.margins.left : 0
|
||||
|
||||
PlasmaCore.IconItem {
|
||||
id: icon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: valid ? Math.min(Math.floor(parent.width/2) * 2, Math.floor(parent.height/2) * 2): 0
|
||||
height: width
|
||||
active: delegate.item.hasOverState && mouse.containsMouse
|
||||
colorGroup: mouse.containsMouse || !button.flat ? PlasmaCore.Theme.ButtonColorGroup : PlasmaCore.Theme.NormalColorGroup
|
||||
}
|
||||
|
||||
Label {
|
||||
id: label
|
||||
|
||||
width: parent.width - icon.width - parent.spacing
|
||||
height: parent.height
|
||||
|
||||
color: mouse.containsMouse || !button.flat ? theme.buttonTextColor : theme.textColor
|
||||
Behavior on color { ColorAnimation { duration: units.shortDuration * 2 } }
|
||||
|
||||
horizontalAlignment: icon.valid ? Text.AlignLeft : Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: delegate.item.hasOverState
|
||||
|
||||
onPressed: internal.userPressed = true
|
||||
onReleased: internal.userPressed = false
|
||||
onCanceled: {
|
||||
internal.userPressed = false
|
||||
delegate.shadowState = "shadow"
|
||||
Tooltip.hideText()
|
||||
}
|
||||
onClicked: internal.clickButton()
|
||||
|
||||
onEntered: {
|
||||
if (delegate.item.hasOverState && !flat && !internal.userPressed && !checked) {
|
||||
delegate.shadowState = "hover"
|
||||
}
|
||||
button.z += 2
|
||||
}
|
||||
onExited: {
|
||||
if (!flat) {
|
||||
if (button.activeFocus) {
|
||||
delegate.shadowState = "focus"
|
||||
} else if (checked) {
|
||||
delegate.shadowState = "hidden"
|
||||
} else {
|
||||
delegate.shadowState = "shadow"
|
||||
}
|
||||
}
|
||||
button.z -= 2
|
||||
Tooltip.hideText()
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: mouse.containsMouse && !pressed && tooltip.length
|
||||
onTriggered: Tooltip.showText(mouse, Qt.point(mouse.mouseX, mouse.mouseY), tooltip)
|
||||
}
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: text
|
||||
Accessible.description: tooltip
|
||||
function accessiblePressAction() {
|
||||
internal.clickButton()
|
||||
}
|
||||
style: Styles.ToolButtonStyle {}
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,8 @@ QtQuickControlStyle.ButtonStyle {
|
||||
}
|
||||
}
|
||||
|
||||
opacity: enabled ? 1.0 : 0.5
|
||||
|
||||
Private.ButtonShadow {
|
||||
anchors.fill: parent
|
||||
state: {
|
||||
|
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2014 by Marco Martin <mart@kde.org>
|
||||
* Copyright 2014 by David Edmundson <davidedmundson@kde.org>
|
||||
*
|
||||
* This program 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, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls.Styles 1.1 as QtQuickControlStyle
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||
import org.kde.plasma.components 2.0 as PlasmaComponents
|
||||
|
||||
import "../private" as Private
|
||||
|
||||
QtQuickControlStyle.ButtonStyle {
|
||||
id: style
|
||||
|
||||
property int minimumWidth
|
||||
property int minimumHeight
|
||||
|
||||
label: RowLayout {
|
||||
id: buttonContent
|
||||
spacing: units.smallSpacing
|
||||
Layout.preferredHeight: Math.max(units.iconSizes.small, label.implicitHeight)
|
||||
|
||||
property real minimumWidth: Layout.minimumWidth + style.padding.left + style.padding.right
|
||||
onMinimumWidthChanged: {
|
||||
if (control.minimumWidth !== undefined) {
|
||||
style.minimumWidth = minimumWidth;
|
||||
control.minimumWidth = minimumWidth;
|
||||
}
|
||||
}
|
||||
|
||||
property real minimumHeight: Layout.preferredHeight + style.padding.top + style.padding.bottom
|
||||
onMinimumHeightChanged: {
|
||||
if (control.minimumHeight !== undefined) {
|
||||
style.minimumHeight = minimumHeight;
|
||||
control.minimumHeight = minimumHeight;
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaCore.IconItem {
|
||||
id: icon
|
||||
source: control.iconName || control.iconSource
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.minimumWidth: valid ? parent.height: 0
|
||||
Layout.maximumWidth: Layout.minimumWidth
|
||||
visible: valid
|
||||
Layout.minimumHeight: Layout.minimumWidth
|
||||
Layout.maximumHeight: Layout.minimumWidth
|
||||
active: control.hovered
|
||||
colorGroup: PlasmaCore.Theme.ButtonColorGroup
|
||||
}
|
||||
|
||||
PlasmaComponents.Label {
|
||||
id: label
|
||||
Layout.minimumWidth: implicitWidth
|
||||
text: control.text
|
||||
font: control.font
|
||||
visible: control.text != ""
|
||||
Layout.fillWidth: true
|
||||
height: parent.height
|
||||
color: theme.buttonTextColor
|
||||
horizontalAlignment: icon.valid ? Text.AlignLeft : Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
PlasmaExtras.ConditionalLoader {
|
||||
id: arrow
|
||||
when: control.menu !== null
|
||||
visible: when
|
||||
Layout.minimumWidth: units.iconSizes.small
|
||||
Layout.maximumWidth: Layout.minimumWidth
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
height: width
|
||||
|
||||
source: Component {
|
||||
PlasmaCore.SvgItem {
|
||||
visible: control.menu !== null
|
||||
anchors.fill: parent
|
||||
svg: PlasmaCore.Svg { imagePath: "widgets/arrows" }
|
||||
elementId: "down-arrow"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Item {
|
||||
|
||||
implicitHeight: Math.floor(Math.max(theme.mSize(theme.defaultFont).height*1.6, style.minimumHeight))
|
||||
|
||||
implicitWidth: {
|
||||
if (control.text.length == 0) {
|
||||
height;
|
||||
} else {
|
||||
Math.max(theme.mSize(theme.defaultFont).width*12, style.minimumWidth);
|
||||
}
|
||||
}
|
||||
|
||||
Private.ButtonShadow {
|
||||
anchors.fill: parent
|
||||
state: {
|
||||
if (control.pressed) {
|
||||
return "hidden"
|
||||
} else if (control.hovered) {
|
||||
return "hover"
|
||||
} else if (control.activeFocus) {
|
||||
return "focus"
|
||||
} else {
|
||||
return "shadow"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//This code is duplicated here and Button and ToolButton
|
||||
//maybe we can make an AbstractButton class?
|
||||
PlasmaCore.FrameSvgItem {
|
||||
id: surfaceNormal
|
||||
anchors.fill: parent
|
||||
imagePath: "widgets/button"
|
||||
prefix: "normal"
|
||||
}
|
||||
|
||||
PlasmaCore.FrameSvgItem {
|
||||
id: surfacePressed
|
||||
anchors.fill: parent
|
||||
imagePath: "widgets/button"
|
||||
prefix: "pressed"
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
state: (control.pressed || control.checked ? "pressed" : (control.hovered ? "hover" : "normal"))
|
||||
|
||||
states: [
|
||||
State { name: "normal"
|
||||
PropertyChanges {
|
||||
target: surfaceNormal
|
||||
opacity: control.flat ? 0 : 1
|
||||
}
|
||||
PropertyChanges {
|
||||
target: surfacePressed
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State { name: "hover"
|
||||
PropertyChanges {
|
||||
target: surfaceNormal
|
||||
opacity: 1
|
||||
}
|
||||
PropertyChanges {
|
||||
target: surfacePressed
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State { name: "pressed"
|
||||
PropertyChanges {
|
||||
target: surfaceNormal
|
||||
opacity: 0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: surfacePressed
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
//Cross fade from pressed to normal
|
||||
ParallelAnimation {
|
||||
NumberAnimation { target: surfaceNormal; property: "opacity"; duration: 100 }
|
||||
NumberAnimation { target: surfacePressed; property: "opacity"; duration: 100 }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Component.onCompleted: {
|
||||
padding.top = surfaceNormal.margins.top
|
||||
padding.left = surfaceNormal.margins.left
|
||||
padding.right = surfaceNormal.margins.right
|
||||
padding.bottom = surfaceNormal.margins.bottom
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user