@@ -0,0 +1,214 @@
1+AbstractButton {
2+ id: root;
3+ property var cardData;
4+ property string artShapeStyle: "inset";
5+ property string backgroundShapeStyle: "inset";
6+ property real fontScale: 1.0;
7+ property var scopeStyle: null;
8+ property int titleAlignment: Text.AlignLeft;
9+ property int fixedHeaderHeight: -1;
10+ property size fixedArtShapeSize: Qt.size(-1, -1);
11+ readonly property string title: cardData && cardData["title"] || "";
12+ property bool asynchronous: true;
13+ property bool showHeader: true;
14+ implicitWidth: childrenRect.width;
15+ enabled: true;
16+
17+Loader {
18+ id: backgroundLoader;
19+ objectName: "backgroundLoader";
20+ anchors.fill: parent;
21+ asynchronous: root.asynchronous;
22+ visible: status == Loader.Ready;
23+ sourceComponent: UbuntuShape {
24+ objectName: "background";
25+ radius: "medium";
26+ aspect: {
27+ switch (root.backgroundShapeStyle) {
28+ case "inset": return UbuntuShape.Inset;
29+ case "shadow": return UbuntuShape.DropShadow;
30+ default:
31+ case "flat": return UbuntuShape.Flat;
32+ }
33+ }
34+ backgroundColor: getColor(0) || "white";
35+ secondaryBackgroundColor: getColor(1) || backgroundColor;
36+ backgroundMode: UbuntuShape.VerticalGradient;
37+ anchors.fill: parent;
38+ source: backgroundImage.source ? backgroundImage : null;
39+ property real luminance: Style.luminance(backgroundColor);
40+ property Image backgroundImage: Image {
41+ objectName: "backgroundImage";
42+ source: {
43+ if (cardData && typeof cardData["background"] === "string") return cardData["background"];
44+ else return "";
45+ }
46+ }
47+ function getColor(index) {
48+ if (cardData && typeof cardData["background"] === "object"
49+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
50+ return cardData["background"]["elements"][index];
51+ } else return index === 0 ? "#E9E9E9" : undefined;
52+ }
53+ }
54+ }
55+readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
56+Item {
57+ id: artShapeHolder;
58+ height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
59+ width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
60+ anchors { horizontalCenter: parent.horizontalCenter; }
61+ Loader {
62+ id: artShapeLoader;
63+ objectName: "artShapeLoader";
64+ readonly property string cardArt: cardData && cardData["art"] || "";
65+ active: cardArt != "";
66+ asynchronous: root.asynchronous;
67+ visible: status == Loader.Ready;
68+ sourceComponent: Item {
69+ id: artShape;
70+ objectName: "artShape";
71+ visible: image.status == Image.Ready;
72+ readonly property alias image: artImage;
73+ ShaderEffectSource {
74+ id: artShapeSource;
75+ sourceItem: artImage;
76+ anchors.centerIn: parent;
77+ width: 1;
78+ height: 1;
79+ hideSource: true;
80+ }
81+ Loader {
82+ anchors.fill: parent;
83+ visible: true;
84+ sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent;
85+ Component {
86+ id: artShapeShapeComponent;
87+ UbuntuShape {
88+ source: artShapeSource;
89+ sourceFillMode: UbuntuShape.PreserveAspectCrop;
90+ radius: "medium";
91+ aspect: {
92+ switch (root.artShapeStyle) {
93+ case "inset": return UbuntuShape.Inset;
94+ case "shadow": return UbuntuShape.DropShadow;
95+ default:
96+ case "flat": return UbuntuShape.Flat;
97+ }
98+ }
99+ }
100+ }
101+ Component {
102+ id: artShapeIconComponent;
103+ ProportionalShape { source: artShapeSource; aspect: UbuntuShape.DropShadow; }
104+ }
105+ }
106+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
107+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : 1;
108+ Component.onCompleted: { updateWidthHeightBindings(); }
109+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
110+ function updateWidthHeightBindings() {
111+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
112+ width = root.fixedArtShapeSize.width;
113+ height = root.fixedArtShapeSize.height;
114+ } else {
115+ width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
116+ height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
117+ }
118+ }
119+ CroppedImageMinimumSourceSize {
120+ id: artImage;
121+ objectName: "artImage";
122+ source: artShapeLoader.cardArt;
123+ asynchronous: root.asynchronous;
124+ width: root.width;
125+ height: width / artShape.aspect;
126+ }
127+ }
128+ }
129+ }
130+readonly property int headerHeight: row.height;
131+Row {
132+ id: row;
133+ objectName: "outerRow";
134+ property real margins: units.gu(1);
135+ spacing: margins;
136+ height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
137+ anchors { top: artShapeHolder.bottom;
138+ topMargin: units.gu(1);
139+left: parent.left;
140+ }
141+ anchors.right: parent.right;
142+ anchors.margins: margins;
143+ anchors.rightMargin: 0;
144+ data: [
145+ CroppedImageMinimumSourceSize {
146+ id: mascotImage;
147+ objectName: "mascotImage";
148+ anchors { verticalCenter: parent.verticalCenter; }
149+ source: cardData && cardData["mascot"] || "";
150+ width: units.gu(6);
151+ height: units.gu(5.625);
152+ horizontalAlignment: Image.AlignHCenter;
153+ verticalAlignment: Image.AlignVCenter;
154+ visible: showHeader;
155+ }
156+,Item {
157+ id: headerTitleContainer;
158+ anchors { verticalCenter: parent.verticalCenter; }
159+ width: parent.width - x;
160+ implicitHeight: titleLabel.height + subtitleLabel.height;
161+ data: [
162+ Label {
163+ id: titleLabel;
164+ objectName: "titleLabel";
165+ anchors { right: parent.right;
166+rightMargin: units.gu(1);
167+left: parent.left;
168+ top: parent.top; }
169+ elide: Text.ElideRight;
170+ fontSize: "small";
171+ wrapMode: Text.Wrap;
172+ maximumLineCount: 2;
173+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
174+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white");
175+ visible: showHeader ;
176+ width: undefined;
177+ text: root.title;
178+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
179+ horizontalAlignment: root.titleAlignment;
180+ }
181+,Label {
182+ id: subtitleLabel;
183+ objectName: "subtitleLabel";
184+ anchors { right: parent.right;
185+ left: parent.left;
186+rightMargin: units.gu(1);
187+top: titleLabel.bottom;
188+ }
189+ anchors.topMargin: units.dp(2);
190+ elide: Text.ElideRight;
191+ maximumLineCount: 1;
192+ fontSize: "x-small";
193+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
194+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white");
195+ visible: titleLabel.visible && titleLabel.text;
196+ text: cardData && cardData["subtitle"] || "";
197+ font.weight: Font.Light;
198+ }
199+
200+ ]
201+ }
202+
203+ ]
204+ }
205+UbuntuShape {
206+ id: touchdown;
207+ objectName: "touchdown";
208+ anchors { fill: backgroundLoader }
209+ visible: root.artShapeStyle != "shadow" && root.artShapeStyle != "icon" && root.pressed;
210+ radius: "medium";
211+ borderSource: "radius_pressed.sci"
212+ }
213+implicitHeight: row.y + row.height + units.gu(1);
214+}