diff --git a/src/controls/Card.qml b/src/controls/Card.qml index f5f893fd..a3aea39b 100644 --- a/src/controls/Card.qml +++ b/src/controls/Card.qml @@ -1,109 +1,109 @@ /* * Copyright 2018 Marco Martin * * 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 02110-1301, USA. */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.5 as Kirigami import "private" /** * This is the standard layout of a Card. * It is recommended to use this class when the concept of Cards is needed * in the application. * This Card has default items as header and footer. The header is an * image that can contain an optional title and icon, accessible via the * banner grouped property. * The footer will show a series of toolbuttons (and eventual overflow menu) * representing the actions list accessible with the list property actions. * It is possible even tough is discouraged to override the footer: * in this case the actions property shouldn't be used. * * @inherits AbstractCard * @since 2.4 */ Kirigami.AbstractCard { id: root /** * actions: list * if the card should provide clickable actions, put them in this property, * they will be put in the footer as a list of ToolButtons plus an optional * overflow menu, when not all of them will fit in the available Card width. */ property list actions /** * actions: hiddenActions * This list of actions is for those you always want in the menu, even if there * is enough space. * @since 2.6 */ property list hiddenActions /** * banner: Image * Gropuped property to control the banner image present in the header, it * has the following sub properties: * * url source: the source for the image, it understands any url * valid for an Image component * * string title: the title for the banner, shown as contrasting * text over the image * * Qt.Alignment titleAlignment: the alignment of the title inside the image, * a combination of flags is supported * (default: Qt.AlignTop | Qt.AlignLeft) * * string titleIcon: the optional icon to put in the banner: * it can be either a freedesktop-compatible icon name (recommended) * or any url supported by Image * * titleLevel: The Kirigami Heading level for the title, it controls the font size, default 1 * * wrapMode: if the header should be able to do wrapping * * It also has the full set of properties a QML Image has, such as sourceSize and fillMode */ readonly property alias banner: bannerImage header: BannerImage { id: bannerImage anchors.leftMargin: -root.leftPadding anchors.topMargin: -root.topPadding anchors.rightMargin: root.headerOrientation == Qt.Vertical ? -root.rightPadding : 0 anchors.bottomMargin: root.headerOrientation == Qt.Horizontal ? -root.bottomPadding : 0 - height: Layout.preferredHeight + //height: Layout.preferredHeight implicitWidth: root.headerOrientation == Qt.Horizontal ? sourceSize.width : Layout.preferredWidth } onHeaderChanged: { if (!header) { return; } header.anchors.leftMargin = Qt.binding(function() {return -root.leftPadding}); header.anchors.topMargin = Qt.binding(function() {return -root.topPadding}); header.anchors.rightMargin = Qt.binding(function() {return root.headerOrientation == Qt.Vertical ? -root.rightPadding : 0}); header.anchors.bottomMargin = Qt.binding(function() {return root.headerOrientation == Qt.Horizontal ? -root.bottomPadding : 0}); } footer: Kirigami.ActionToolBar { id: actionsToolBar actions: root.actions position: Controls.ToolBar.Footer hiddenActions: root.hiddenActions visible: root.footer == actionsToolBar } } diff --git a/src/controls/private/BannerImage.qml b/src/controls/private/BannerImage.qml index 1706d488..7ce46c4f 100644 --- a/src/controls/private/BannerImage.qml +++ b/src/controls/private/BannerImage.qml @@ -1,149 +1,152 @@ /* * Copyright 2018 Marco Martin * * 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 02110-1301, USA. */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 as Kirigami /** * This Component is used as the header of GlobalDrawer and as the header * of Card, It can be accessed there as a grouped property but can never * be instantiated directly */ Image { id: root /* * FIXME: compatibility */ property alias imageSource: root.source property alias iconSource: root.titleIcon /** * title: string * A title to be displayed on top of the image */ property alias title: heading.text /** * icon: var * An icon to be displayed alongside the title. * It can be a QIcon, a fdo-compatible icon name, or any url understood by Image */ property alias titleIcon: headingIcon.source /** * titleAlignment: Qt.Alignment */ property int titleAlignment: Qt.AlignTop | Qt.AlignLeft /** * titleLevel: a Kirigami Heading level, default 1 */ property alias titleLevel: heading.level /** * wrapMode: if the header should be able to do wrapping */ property alias titleWrapMode: heading.wrapMode property int leftPadding: headingIcon.valid ? Kirigami.Units.smallSpacing * 2 : Kirigami.Units.largeSpacing property int topPadding: headingIcon.valid ? Kirigami.Units.smallSpacing * 2 : Kirigami.Units.largeSpacing property int rightPadding: headingIcon.valid ? Kirigami.Units.smallSpacing * 2 : Kirigami.Units.largeSpacing property int bottomPadding: headingIcon.valid ? Kirigami.Units.smallSpacing * 2 : Kirigami.Units.largeSpacing Layout.fillWidth: true Layout.preferredWidth: titleLayout.implicitWidth || sourceSize.width - Layout.preferredHeight: source != "" ? width/(sourceSize.width / sourceSize.height) : Layout.minimumHeight + Layout.preferredHeight: titleLayout.completed && source != "" ? width/(sourceSize.width / sourceSize.height) : Layout.minimumHeight Layout.minimumHeight: titleLayout.implicitHeight > 0 ? titleLayout.implicitHeight + Kirigami.Units.smallSpacing * 2 : 0 property int implicitWidth: Layout.preferredWidth readonly property bool empty: bannerImage.title !== undefined && bannerImage.title.length === 0 && bannerImage.source !== undefined && bannerImage.source.length === 0 && bannerImage.titleIcon !== undefined &&bannerImage.titleIcon.length === 0 - - fillMode: Image.PreserveAspectCrop asynchronous: true + Component.onCompleted: { + titleLayout.completed = true; + } + LinearGradient { anchors { left: parent.left right: parent.right top: root.status != Image.Ready || (root.titleAlignment & Qt.AlignTop) ? parent.top : undefined bottom: root.status != Image.Ready || (root.titleAlignment & Qt.AlignBottom) ? parent.bottom : undefined } visible: root.source != "" && root.title != "" && ((root.titleAlignment & Qt.AlignTop) || (root.titleAlignment & Qt.AlignBottom)) height: Math.min(parent.height, titleLayout.height * 2) start: Qt.point(0, 0) end: Qt.point(0, height) gradient: Gradient { GradientStop { position: (root.titleAlignment & Qt.AlignTop) ? 0.0 : 1.0 color: Qt.rgba(0, 0, 0, 0.8) } GradientStop { position: (root.titleAlignment & Qt.AlignTop) ? 1.0 : 0.0 color: Qt.rgba(0, 0, 0, root.status == Image.Ready ? 0 : 0.3) } } } RowLayout { id: titleLayout + property bool completed: false anchors { left: root.titleAlignment & Qt.AlignLeft ? parent.left : undefined top: root.titleAlignment & Qt.AlignTop ? parent.top : undefined right: root.titleAlignment & Qt.AlignRight ? parent.right : undefined bottom: root.titleAlignment & Qt.AlignBottom ? parent.bottom : undefined horizontalCenter: root.titleAlignment & Qt.AlignHCenter ? parent.horizontalCenter : undefined verticalCenter: root.titleAlignment & Qt.AlignVCenter ? parent.verticalCenter : undefined leftMargin: root.leftPadding topMargin: root.topPadding rightMargin: root.rightPadding bottomMargin: root.bottomPadding } width: Math.min(implicitWidth, parent.width -root.leftPadding - root.rightPadding) height: Math.min(implicitHeight, parent.height - root.topPadding - root.bottomPadding) Kirigami.Icon { id: headingIcon Layout.minimumWidth: Kirigami.Units.iconSizes.large Layout.minimumHeight: width visible: valid isMask: false } Kirigami.Heading { id: heading Layout.fillWidth: true Layout.fillHeight: true visible: text.length > 0 level: 1 color: source != "" ? "white" : Kirigami.Theme.textColor wrapMode: Text.NoWrap elide: Text.ElideRight } } } diff --git a/src/controls/templates/AbstractCard.qml b/src/controls/templates/AbstractCard.qml index 9f670f3f..66882c6a 100644 --- a/src/controls/templates/AbstractCard.qml +++ b/src/controls/templates/AbstractCard.qml @@ -1,165 +1,167 @@ /* * Copyright 2018 Marco Martin * * 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 02110-1301, USA. */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtQuick.Templates 2.0 as T import org.kde.kirigami 2.4 as Kirigami /** * A AbstractCard is the base for cards. A Card is a visual object that serves * as an entry point for more detailed information. An abstractCard is empty, * providing just the look and the base properties and signals for an ItemDelegate. * It can be filled with any custom layout of items, its content is organized * in 3 properties: header, contentItem and footer. * Use this only when you need particular custom contents, for a standard layout * for cards, use the Card component. * * @see Card * @inherits QtQuick.Templates.ItemDelegate * @since 2.4 */ T.ItemDelegate { id: root /** * header: Item * This item serves as header, it will be put either on top if headerOrientation * is Qt.Vertical(default) or on the left if it's Qt.Horizontal */ property Item header /** * headerOrientation: Qt.Orientation * If Qt.Vertical the header will be positioned on top(default), * if Qt.Horizontal will be positioned on the left (or right if an RTL layout is used) */ property int headerOrientation: Qt.Vertical /** * footer: Item * This item serves as footer, and it will be positioned at the bottom of the card. */ property Item footer /** * showClickFeedback: bool * if true, when clicking or tapping on the card area, the card will be colored * to show a visual click feedback. * Use this if you want to do an action in the onClicked signal handler of the card. */ property bool showClickFeedback: false Layout.fillWidth: true implicitWidth: Math.max(background.implicitWidth, mainLayout.implicitWidth) + leftPadding + rightPadding implicitHeight: mainLayout.implicitHeight + topPadding + bottomPadding hoverEnabled: !Kirigami.Settings.tabletMode && showClickFeedback //if it's in a CardLayout, try to expand horizontal cards to both columns Layout.columnSpan: headerOrientation == Qt.Horizontal && parent.hasOwnProperty("columns") ? parent.columns : 1 Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.View topPadding: Kirigami.Units.largeSpacing leftPadding: Kirigami.Units.largeSpacing bottomPadding: Kirigami.Units.largeSpacing rightPadding: Kirigami.Units.largeSpacing GridLayout { id: mainLayout rowSpacing: root.topPadding columnSpacing: root.leftPadding anchors { top: parent.top left: parent.left right: parent.right leftMargin: root.leftPadding topMargin: root.topPadding rightMargin: root.rightPadding - //never anchor bottom, to not have binding loops + bottom:parent.bottom + bottomMargin: root.bottomPadding } columns: headerOrientation == Qt.Vertical ? 1 : 2 function preferredHeight(item) { if (!item) { return 0; } if (item.Layout.preferredHeight > 0) { return item.Layout.preferredHeight; } return item.implicitHeight } Item { id: headerParent Layout.fillWidth: true Layout.fillHeight: root.headerOrientation == Qt.Horizontal Layout.rowSpan: root.headerOrientation == Qt.Vertical ? 1 : 2 Layout.preferredWidth: header ? header.implicitWidth : 0 Layout.preferredHeight: root.headerOrientation == Qt.Vertical ? mainLayout.preferredHeight(header) : -1 } Item { id: contentItemParent Layout.fillWidth: true + Layout.fillHeight: true Layout.preferredWidth: contentItem ? contentItem.implicitWidth : 0 Layout.preferredHeight: mainLayout.preferredHeight(contentItem) } Item { id: footerParent Layout.fillWidth: true Layout.preferredWidth: footer ? footer.implicitWidth : 0 Layout.preferredHeight: mainLayout.preferredHeight(footer) } } //BEGIN signal handlers onContentItemChanged: { if (!contentItem) { return; } contentItem.parent = contentItemParent; contentItem.anchors.fill = contentItemParent; } onHeaderChanged: { if (!header) { return; } header.parent = headerParent; header.anchors.fill = headerParent; } onFooterChanged: { if (!footer) { return; } //make the footer always looking it's at the bottom of the card footer.parent = footerParent; footer.anchors.left = footerParent.left; footer.anchors.top = footerParent.top; footer.anchors.right = footerParent.right; footer.anchors.topMargin = Qt.binding(function() {return (root.height - root.bottomPadding - root.topPadding) - (footerParent.y + footerParent.height)}); } Component.onCompleted: { contentItemChanged(); } //END signal handlers }