@@ -0,0 +1,150 @@
1+AbstractButton {
2+ id: root;
3+ property var components;
4+ property var cardData;
5+ property string artShapeStyle: "inset";
6+ property string backgroundShapeStyle: "inset";
7+ property real fontScale: 1.0;
8+ property var scopeStyle: null;
9+ property int titleAlignment: Text.AlignLeft;
10+ property int fixedHeaderHeight: -1;
11+ property size fixedArtShapeSize: Qt.size(-1, -1);
12+ readonly property string title: cardData && cardData["title"] || "";
13+ property bool asynchronous: true;
14+ property bool showHeader: true;
15+ implicitWidth: childrenRect.width;
16+ enabled: true;
17+signal clicked(var result);
18+readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
19+Item {
20+ id: artShapeHolder;
21+ height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
22+ width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
23+ anchors { horizontalCenter: parent.horizontalCenter; }
24+ Loader {
25+ id: artShapeLoader;
26+ objectName: "artShapeLoader";
27+ active: cardData && cardData["art"] || false;
28+ asynchronous: root.asynchronous;
29+ visible: status == Loader.Ready;
30+ sourceComponent: Item {
31+ id: artShape;
32+ objectName: "artShape";
33+ readonly property bool doShapeItem: components["art"]["conciergeMode"] !== true;
34+ visible: image.status == Image.Ready;
35+ readonly property alias image: artImage;
36+ ShaderEffectSource {
37+ id: artShapeSource;
38+ sourceItem: artImage;
39+ anchors.centerIn: parent;
40+ width: 1;
41+ height: 1;
42+ hideSource: doShapeItem;
43+ }
44+ Loader {
45+ anchors.fill: parent;
46+ visible: artShape.doShapeItem;
47+ sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent;
48+ Component {
49+ id: artShapeShapeComponent;
50+ UbuntuShape {
51+ source: artShapeSource;
52+ sourceFillMode: UbuntuShape.PreserveAspectCrop;
53+ radius: "medium";
54+ aspect: {
55+ switch (root.artShapeStyle) {
56+ case "inset": return UbuntuShape.Inset;
57+ case "shadow": return UbuntuShape.DropShadow;
58+ default:
59+ case "flat": return UbuntuShape.Flat;
60+ }
61+ }
62+ }
63+ }
64+ Component {
65+ id: artShapeIconComponent;
66+ ProportionalShape { source: artShapeSource; aspect: UbuntuShape.DropShadow; }
67+ }
68+ }
69+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
70+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
71+ Component.onCompleted: { updateWidthHeightBindings(); }
72+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
73+ function updateWidthHeightBindings() {
74+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
75+ width = root.fixedArtShapeSize.width;
76+ height = root.fixedArtShapeSize.height;
77+ } else {
78+ width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
79+ height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
80+ }
81+ }
82+ CroppedImageMinimumSourceSize {
83+ id: artImage;
84+ objectName: "artImage";
85+ source: cardData && cardData["art"] || "";
86+ asynchronous: root.asynchronous;
87+ width: root.width;
88+ height: width / artShape.aspect;
89+ }
90+ }
91+ }
92+ }
93+readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
94+Label {
95+ id: titleLabel;
96+ objectName: "titleLabel";
97+ anchors { right: parent.right;
98+ left: parent.left;
99+ top: artShapeHolder.bottom;
100+ topMargin: units.gu(1);
101+ }
102+ elide: Text.ElideRight;
103+ fontSize: "small";
104+ wrapMode: Text.Wrap;
105+ maximumLineCount: 2;
106+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
107+ color: root.scopeStyle ? root.scopeStyle.foreground : theme.palette.normal.baseText;
108+ visible: showHeader ;
109+ width: undefined;
110+ text: root.title;
111+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
112+ horizontalAlignment: root.titleAlignment;
113+ }
114+Label {
115+ id: subtitleLabel;
116+ objectName: "subtitleLabel";
117+ anchors { left: titleLabel.left;
118+ leftMargin: titleLabel.leftMargin;
119+ right: titleLabel.right;
120+ top: titleLabel.bottom;
121+ }
122+ anchors.topMargin: units.dp(2);
123+ elide: Text.ElideRight;
124+ maximumLineCount: 1;
125+ fontSize: "x-small";
126+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
127+ color: root.scopeStyle ? root.scopeStyle.foreground : theme.palette.normal.baseText;
128+ visible: titleLabel.visible && titleLabel.text;
129+ text: cardData && cardData["subtitle"] || "";
130+ font.weight: Font.Light;
131+ }
132+CardSocialAttributes {
133+ id: socialAttributesRow;
134+ objectName: "socialAttributesRow";
135+ anchors { top: subtitleLabel.bottom; left: parent.left; right: parent.right; topMargin: units.gu(1); }
136+ color: root.scopeStyle ? root.scopeStyle.foreground : theme.palette.normal.baseText;
137+ fontScale: root.fontScale;
138+ model: cardData && cardData["socialAttributes"];
139+ onClicked: root.clicked(result);
140+}
141+UbuntuShape {
142+ id: touchdown;
143+ objectName: "touchdown";
144+ anchors { fill: artShapeHolder }
145+ visible: root.artShapeStyle != "shadow" && root.artShapeStyle != "icon" && root.pressed;
146+ radius: "medium";
147+ borderSource: "radius_pressed.sci"
148+}
149+implicitHeight: socialAttributesRow.y + socialAttributesRow.height + units.gu(1);
150+}