on xenial help sign in
Redo text color threshold on backgrounds
f7af552Michał Sawicz
plugins/Dash/CardCreator.js+2−2
@@ -439,14 +439,14 @@ function cardString(template, components) {
439 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
439 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
440 }
440 }
441
441
442− var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText)';
442+ var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white")';
443
443
444 var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
444 var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
445 var titleSubtitleCode = '';
445 var titleSubtitleCode = '';
446 if (hasTitle) {
446 if (hasTitle) {
447 var titleColor;
447 var titleColor;
448 if (headerAsOverlay) {
448 if (headerAsOverlay) {
449− titleColor = 'overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText)';
449+ titleColor = 'root.scopeStyle ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white")';
450 } else if (hasSummary) {
450 } else if (hasSummary) {
451 titleColor = 'summary.color';
451 titleColor = 'summary.color';
452 } else if (hasBackground) {
452 } else if (hasBackground) {
plugins/Dash/ScopeStyle.qml+20−24
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.2
17import QtQuick 2.2
18import Utils 0.1
18import Utils 0.1
19+import Ubuntu.Components 1.1
19
20
20/*! \brief Helper for processing scope customization options.
21/*! \brief Helper for processing scope customization options.
21
22
@@ -40,31 +41,17 @@ QtObject {
40 /// Luminance of the background color
41 /// Luminance of the background color
41 readonly property real backgroundLuminance: background ? Style.luminance(background) : Style.luminance(d.defaultLight)
42 readonly property real backgroundLuminance: background ? Style.luminance(background) : Style.luminance(d.defaultLight)
42
43
43− /*! \brief Luminance threshold for switching between fore and background color
44+ /*! \brief Get the most contrasting available color based on luminance
44−
45+ *
45− \note If background colour is not fully opaque, the defaultLightLuminance it's taken into account instead of it.
46+ * If background color is transparent, theme provided colors are taken into account
46 */
47 */
47− readonly property real threshold: background.a !== 1.0 ? (foregroundLuminance + d.defaultLightLuminance) / 2
48+ function getTextColor(luminance) {
48− : (foregroundLuminance + backgroundLuminance) / 2
49+ if (Math.abs(foregroundLuminance - luminance) >
49−
50+ Math.abs(d.opaqueBackgroundLuminance - luminance)) {
50− /*! \brief The lighter of foreground and background colors
51−
52− \note If background color is not fully opaque, it's not taken into account
53− and defaults to the theme-provided light color.
54− */
55− readonly property color light: {
56− if (background.a !== 1.0) return foregroundLuminance > d.defaultLightLuminance ? foreground : d.defaultLight;
57− return foregroundLuminance > backgroundLuminance ? foreground : background;
51+ return foreground;
58− }
52+ } else {
59−
53+ return d.opaqueBackground;
60− /*! \brief The darker of foreground and background colors
54+ }
61−
62− \note If background color is not fully opaque, it's not taken into account
63− and defaults to the theme-provided dark color.
64− */
65− readonly property color dark: {
66− if (background.a !== 1.0) return foregroundLuminance < d.defaultDarkLuminance ? foreground : d.defaultDark;
67− return foregroundLuminance < backgroundLuminance ? foreground : background;
68 }
55 }
69
56
70 /// Source of the logo image for the header
57 /// Source of the logo image for the header
@@ -93,6 +80,15 @@ QtObject {
93 readonly property real defaultLightLuminance: Style.luminance(defaultLight)
80 readonly property real defaultLightLuminance: Style.luminance(defaultLight)
94 readonly property real defaultDarkLuminance: Style.luminance(defaultDark)
81 readonly property real defaultDarkLuminance: Style.luminance(defaultDark)
95
82
83+ readonly property color opaqueBackground: {
84+ background.a > 0 ?
85+ background :
86+ (Math.abs(foregroundLuminance - defaultLightLuminance) >
87+ Math.abs(foregroundLuminance - defaultDarkLuminance)) ?
88+ defaultLight : defaultDark
89+ }
90+ readonly property real opaqueBackgroundLuminance: Style.luminance(opaqueBackground)
91+
96 readonly property var headerStyle: "page-header" in style ? style["page-header"] : { }
92 readonly property var headerStyle: "page-header" in style ? style["page-header"] : { }
97 }
93 }
98 //! @endcond
94 //! @endcond
tests/plugins/Dash/cardcreator/2.res+2−2
@@ -94,7 +94,7 @@ Image {
94 wrapMode: Text.Wrap;
94 wrapMode: Text.Wrap;
95 maximumLineCount: 2;
95 maximumLineCount: 2;
96 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
96 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
97− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
97+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
98 visible: showHeader ;
98 visible: showHeader ;
99 text: root.title;
99 text: root.title;
100 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
100 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -112,7 +112,7 @@ Image {
112 elide: Text.ElideRight;
112 elide: Text.ElideRight;
113 fontSize: "small";
113 fontSize: "small";
114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
115+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
116 visible: titleLabel.visible && titleLabel.text;
116 visible: titleLabel.visible && titleLabel.text;
117 text: cardData && cardData["subtitle"] || "";
117 text: cardData && cardData["subtitle"] || "";
118 font.weight: Font.Light;
118 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/5.res+2−2
@@ -113,7 +113,7 @@ Label {
113 wrapMode: Text.Wrap;
113 wrapMode: Text.Wrap;
114 maximumLineCount: 2;
114 maximumLineCount: 2;
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116− color: overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
116+ color: root.scopeStyle ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
117 visible: showHeader && overlayLoader.active;
117 visible: showHeader && overlayLoader.active;
118 text: root.title;
118 text: root.title;
119 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
119 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -131,7 +131,7 @@ Label {
131 elide: Text.ElideRight;
131 elide: Text.ElideRight;
132 fontSize: "small";
132 fontSize: "small";
133 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
133 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
134− color: overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
134+ color: root.scopeStyle ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
135 visible: titleLabel.visible && titleLabel.text;
135 visible: titleLabel.visible && titleLabel.text;
136 text: cardData && cardData["subtitle"] || "";
136 text: cardData && cardData["subtitle"] || "";
137 font.weight: Font.Light;
137 font.weight: Font.Light;
tests/plugins/Dash/cardcreator/6.res+3−3
@@ -70,7 +70,7 @@ Item {
70 wrapMode: Text.Wrap;
70 wrapMode: Text.Wrap;
71 maximumLineCount: 2;
71 maximumLineCount: 2;
72 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
72 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
73− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
73+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
74 visible: showHeader ;
74 visible: showHeader ;
75 text: root.title;
75 text: root.title;
76 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
76 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -88,7 +88,7 @@ Item {
88 elide: Text.ElideRight;
88 elide: Text.ElideRight;
89 fontSize: "small";
89 fontSize: "small";
90 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
90 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
91+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
92 visible: titleLabel.visible && titleLabel.text;
92 visible: titleLabel.visible && titleLabel.text;
93 text: cardData && cardData["subtitle"] || "";
93 text: cardData && cardData["subtitle"] || "";
94 font.weight: Font.Light;
94 font.weight: Font.Light;
@@ -103,7 +103,7 @@ Item {
103 rightMargin: units.gu(1);
103 rightMargin: units.gu(1);
104 }
104 }
105 source: cardData && cardData["emblem"] || "";
105 source: cardData && cardData["emblem"] || "";
106− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
106+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
107 width: height;
107 width: height;
108 height: source != "" ? titleLabel.font.pixelSize : 0;
108 height: source != "" ? titleLabel.font.pixelSize : 0;
109 }
109 }
tests/plugins/Dash/cardcreator/7.res+3−3
@@ -95,7 +95,7 @@ Image {
95 wrapMode: Text.Wrap;
95 wrapMode: Text.Wrap;
96 maximumLineCount: 2;
96 maximumLineCount: 2;
97 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
97 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
98− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
98+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
99 visible: showHeader ;
99 visible: showHeader ;
100 text: root.title;
100 text: root.title;
101 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
101 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
@@ -113,7 +113,7 @@ Image {
113 elide: Text.ElideRight;
113 elide: Text.ElideRight;
114 fontSize: "small";
114 fontSize: "small";
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
116+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
117 visible: titleLabel.visible && titleLabel.text;
117 visible: titleLabel.visible && titleLabel.text;
118 text: cardData && cardData["subtitle"] || "";
118 text: cardData && cardData["subtitle"] || "";
119 font.weight: Font.Light;
119 font.weight: Font.Light;
@@ -127,7 +127,7 @@ Image {
127 rightMargin: units.gu(1);
127 rightMargin: units.gu(1);
128 top: subtitleLabel.bottom;
128 top: subtitleLabel.bottom;
129 }
129 }
130− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : Theme.palette.normal.baseText);
130+ color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
131 model: cardData && cardData["attributes"];
131 model: cardData && cardData["attributes"];
132 }
132 }
133
133
tests/plugins/Dash/tst_ScopeStyle.qml+21−37
@@ -82,48 +82,32 @@ Rectangle {
82 compare(tool.backgroundLuminance.toFixed(4), data.luminance.toFixed(4));
82 compare(tool.backgroundLuminance.toFixed(4), data.luminance.toFixed(4));
83 }
83 }
84
84
85− function test_threshold_data() {
85+ function test_getTextColor_data() {
86 return [
86 return [
87− { tag: "default", index: 0, threshold: 0.6824 },
87+ { tag: "default on black", background: "black", index: 0, textColor: "white" },
88− { tag: "red on black", index: 1, threshold: 0.1063 },
88+ { tag: "default on lightgrey", background: "lightgrey", index: 0, textColor: UbuntuColors.darkGrey },
89− { tag: "green on white", index: 2, threshold: 0.6795 },
89+ { tag: "default on white", background: "white", index: 0, textColor: UbuntuColors.darkGrey },
90− { tag: "blue on darkgrey", index: 3, threshold: 0.3675 },
90+ { tag: "default on yellow", background: "yellow", index: 0, textColor: UbuntuColors.darkGrey },
91+ { tag: "red/black on black", background: "black", index: 1, textColor: "red" },
92+ { tag: "red/black on lightgrey", background: "lightgrey", index: 1, textColor: "black" },
93+ { tag: "red/black on white", background: "white", index: 1, textColor: "black" },
94+ { tag: "red/black on yellow", background: "yellow", index: 1, textColor: "black" },
95+ { tag: "green/white on black", background: "black", index: 2, textColor: "white" },
96+ { tag: "green/white on lightgrey", background: "lightgrey", index: 2, textColor: "green" },
97+ { tag: "green/white on white", background: "white", index: 2, textColor: "green" },
98+ { tag: "green/white on yellow", background: "yellow", index: 2, textColor: "green" },
99+ { tag: "blue/darkgrey on black", background: "black", index: 3, textColor: "darkgrey" },
100+ { tag: "blue/darkgrey on lightgrey", background: "lightgrey", index: 3, textColor: "blue" },
101+ { tag: "blue/darkgrey on white", background: "white", index: 3, textColor: "blue" },
102+ { tag: "blue/darkgrey on yellow", background: "yellow", index: 3, textColor: "blue" },
91 ];
103 ];
92 }
104 }
93
105
94− function test_threshold(data) {
106+ function test_getTextColor(data) {
95 tool.style = testCase.styles[data.index];
107 tool.style = testCase.styles[data.index];
96− compare(tool.threshold.toFixed(4), data.threshold.toFixed(4), "Luminance threshold was incorrect.");
108+ var textColor = tool.getTextColor(Style.luminance(data.background));
97− }
98−
99− function test_light_data() {
100− return [
101− { tag: "default", index: 0, light: "white" },
102− { tag: "red on black", index: 1, light: "red" },
103− { tag: "green on white", index: 2, light: "white" },
104− { tag: "blue on darkgrey", index: 3, light: "darkgrey" },
105− ];
106− }
107−
108− function test_light(data) {
109− tool.style = testCase.styles[data.index];
110− verify(Qt.colorEqual(tool.light, data.light),
111− "Light color not equal: %1 != %2".arg(tool.light).arg(data.light));
112− }
113−
114− function test_dark_data() {
115− return [
116− { tag: "default", index: 0, dark: UbuntuColors.darkGrey },
117− { tag: "red on black", index: 1, dark: "black" },
118− { tag: "green on white", index: 2, dark: "green" },
119− { tag: "blue on darkgrey", index: 3, dark: "blue" },
120− ];
121− }
122−
123− function test_dark(data) {
124− tool.style = testCase.styles[data.index];
125− verify(Qt.colorEqual(tool.dark, data.dark),
109+ verify(Qt.colorEqual(textColor, data.textColor),
126− "Dark color not equal: %1 != %2".arg(tool.dark).arg(data.dark));
110+ "TextColor not equal: %1 != %2".arg(textColor).arg(data.textColor));
127 }
111 }
128
112
129 function test_headerLogo_data() {
113 function test_headerLogo_data() {