Skip to content
This repository was archived by the owner on Feb 25, 2024. It is now read-only.

Commit efc0f16

Browse files
authored
Import YaruColorExtension from yaru_colors.dart (#341)
1 parent 552f0a4 commit efc0f16

File tree

2 files changed

+300
-0
lines changed

2 files changed

+300
-0
lines changed

lib/src/colors.dart

+99
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,102 @@ class YaruColors {
109109
/// Xubuntu Blue
110110
static const Color xubuntuBlue = Color(0xFF0044AA);
111111
}
112+
113+
/// Set of useful methods when working with [Color]
114+
extension YaruColorExtension on Color {
115+
/// Scale color attributes relatively to current ones.
116+
/// [alpha], [hue], [saturation] and [lightness] values must be clamped between -1.0 and 1.0
117+
Color scale({
118+
double alpha = 0.0,
119+
double hue = 0.0,
120+
double saturation = 0.0,
121+
double lightness = 0.0,
122+
}) {
123+
assert(alpha >= -1.0 && alpha <= 1.0);
124+
assert(hue >= -1.0 && hue <= 1.0);
125+
assert(saturation >= -1.0 && saturation <= 1.0);
126+
assert(lightness >= -1.0 && lightness <= 1.0);
127+
128+
final hslColor = _getPatchedHslColor();
129+
130+
double scale(double value, double amount, [double upperLimit = 1.0]) {
131+
var result = value;
132+
133+
if (amount > 0) {
134+
result = value + (upperLimit - value) * amount;
135+
} else if (amount < 0) {
136+
result = value + value * amount;
137+
}
138+
139+
return result.clamp(0.0, upperLimit);
140+
}
141+
142+
return hslColor
143+
.withAlpha(scale(opacity, alpha))
144+
.withHue(scale(hslColor.hue, hue, 360.0))
145+
.withSaturation(scale(hslColor.saturation, saturation))
146+
.withLightness(scale(hslColor.lightness, lightness))
147+
.toColor();
148+
}
149+
150+
/// Adjust color attributes by the given values.
151+
/// [alpha], [saturation] and [lightness] values must be clamped between -1.0 and 1.0
152+
/// [hue] value must be clamped between -360.0 and 360.0
153+
Color adjust({
154+
double alpha = 0.0,
155+
double hue = 0.0,
156+
double saturation = 0.0,
157+
double lightness = 0.0,
158+
}) {
159+
assert(alpha >= -1.0 && alpha <= 1.0);
160+
assert(hue >= -360.0 && hue <= 360.0);
161+
assert(saturation >= -1.0 && saturation <= 1.0);
162+
assert(lightness >= -1.0 && lightness <= 1.0);
163+
164+
final hslColor = _getPatchedHslColor();
165+
166+
double adjust(double value, double amount, [double upperLimit = 1.0]) {
167+
return (value + amount).clamp(0.0, upperLimit);
168+
}
169+
170+
return hslColor
171+
.withAlpha(adjust(hslColor.alpha, alpha))
172+
.withHue(adjust(hslColor.hue, hue, 360.0))
173+
.withSaturation(adjust(hslColor.saturation, saturation))
174+
.withLightness(adjust(hslColor.lightness, lightness))
175+
.toColor();
176+
}
177+
178+
/// Return a copy of this color with attributes replaced by given values.
179+
/// [alpha], [saturation] and [lightness] values must be clamped between 0.0 and 1.0
180+
/// [hue] value must be clamped between 0.0 and 360.0
181+
Color copyWith({
182+
double? alpha,
183+
double? hue,
184+
double? saturation,
185+
double? lightness,
186+
}) {
187+
assert(alpha == null || (alpha >= 0.0 && alpha <= 1.0));
188+
assert(hue == null || (hue >= 0.0 && hue <= 360.0));
189+
assert(saturation == null || (saturation >= 0.0 && saturation <= 1.0));
190+
assert(lightness == null || (lightness >= 0.0 && lightness <= 1.0));
191+
192+
final hslColor = _getPatchedHslColor();
193+
194+
return hslColor
195+
.withAlpha(alpha ?? hslColor.alpha)
196+
.withHue(hue ?? hslColor.hue)
197+
.withSaturation(saturation ?? hslColor.saturation)
198+
.withLightness(lightness ?? hslColor.lightness)
199+
.toColor();
200+
}
201+
202+
HSLColor _getPatchedHslColor() {
203+
final hslColor = HSLColor.fromColor(this);
204+
205+
// A pure dark color have saturation level at 1.0, which results in red when lighten it.
206+
// We reset this value to 0.0, so the result is desaturated as expected:
207+
return hslColor
208+
.withSaturation(hslColor.lightness == 0.0 ? 0.0 : hslColor.saturation);
209+
}
210+
}

test/yaru_color_extension_test.dart

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
import 'package:yaru/src/colors.dart';
4+
5+
final Matcher throwsAssertionError = throwsA(isA<AssertionError>());
6+
final Color midColor = const HSLColor.fromAHSL(.5, 180, .5, .5).toColor();
7+
8+
void main() {
9+
group('Color.scale() test -', () {
10+
test('With out of range amount', () {
11+
expect(() => midColor.scale(alpha: -1.1), throwsAssertionError);
12+
expect(() => midColor.scale(alpha: 1.1), throwsAssertionError);
13+
expect(() => midColor.scale(hue: -1.1), throwsAssertionError);
14+
expect(() => midColor.scale(hue: 1.1), throwsAssertionError);
15+
expect(() => midColor.scale(saturation: -1.1), throwsAssertionError);
16+
expect(() => midColor.scale(saturation: 1.1), throwsAssertionError);
17+
expect(() => midColor.scale(lightness: -1.1), throwsAssertionError);
18+
expect(() => midColor.scale(lightness: 1.1), throwsAssertionError);
19+
});
20+
test('With clamped amount', () {
21+
expect(
22+
midColor.scale(alpha: -1.0),
23+
const Color(0x0040bfbf),
24+
);
25+
expect(
26+
midColor.scale(alpha: 1.0),
27+
const Color(0xff40bfbf),
28+
);
29+
expect(
30+
midColor.scale(hue: -1.0),
31+
const Color(0x80bf4040),
32+
);
33+
expect(
34+
midColor.scale(hue: 1.0),
35+
const Color(0x80bf4040),
36+
);
37+
expect(
38+
midColor.scale(saturation: -1.0),
39+
const Color(0x80808080),
40+
);
41+
expect(
42+
midColor.scale(saturation: 1.0),
43+
const Color(0x8000ffff),
44+
);
45+
expect(
46+
midColor.scale(lightness: -1.0),
47+
const Color(0x80000000),
48+
);
49+
expect(
50+
midColor.scale(lightness: 1.0),
51+
const Color(0x80ffffff),
52+
);
53+
});
54+
test('With medium amount', () {
55+
expect(
56+
midColor.scale(alpha: -0.5),
57+
const Color(0x4040bfbf),
58+
);
59+
expect(
60+
midColor.scale(alpha: 0.5),
61+
const Color(0xc040bfbf),
62+
);
63+
expect(
64+
midColor.scale(hue: -0.5),
65+
const Color(0x8080bf40),
66+
);
67+
expect(
68+
midColor.scale(hue: 0.5),
69+
const Color(0x808040bf),
70+
);
71+
expect(
72+
midColor.scale(saturation: -0.5),
73+
const Color(0x80609f9f),
74+
);
75+
expect(
76+
midColor.scale(saturation: 0.5),
77+
const Color(0x8020dfdf),
78+
);
79+
expect(
80+
midColor.scale(lightness: -0.5),
81+
const Color(0x80206060),
82+
);
83+
expect(
84+
midColor.scale(lightness: 0.5),
85+
const Color(0x80a0dfdf),
86+
);
87+
});
88+
});
89+
90+
group('Color.adjust() test -', () {
91+
test('With out of range amount', () {
92+
expect(() => midColor.adjust(alpha: -1.1), throwsAssertionError);
93+
expect(() => midColor.adjust(alpha: 1.1), throwsAssertionError);
94+
expect(() => midColor.adjust(hue: -360.1), throwsAssertionError);
95+
expect(() => midColor.adjust(hue: 360.1), throwsAssertionError);
96+
expect(() => midColor.adjust(saturation: -1.1), throwsAssertionError);
97+
expect(() => midColor.adjust(saturation: 1.1), throwsAssertionError);
98+
expect(() => midColor.adjust(lightness: -1.1), throwsAssertionError);
99+
expect(() => midColor.adjust(lightness: 1.1), throwsAssertionError);
100+
});
101+
test('With clamped amount', () {
102+
expect(
103+
midColor.adjust(alpha: -1.0),
104+
const Color(0x0040bfbf),
105+
);
106+
expect(
107+
midColor.adjust(alpha: 1.0),
108+
const Color(0xff40bfbf),
109+
);
110+
expect(
111+
midColor.adjust(hue: -180),
112+
const Color(0x80bf4040),
113+
);
114+
expect(
115+
midColor.adjust(hue: 180),
116+
const Color(0x80bf4040),
117+
);
118+
expect(
119+
midColor.adjust(saturation: -1.0),
120+
const Color(0x80808080),
121+
);
122+
expect(
123+
midColor.adjust(saturation: 1.0),
124+
const Color(0x8000ffff),
125+
);
126+
expect(
127+
midColor.adjust(lightness: -1.0),
128+
const Color(0x80000000),
129+
);
130+
expect(
131+
midColor.adjust(lightness: 1.0),
132+
const Color(0x80ffffff),
133+
);
134+
});
135+
test('With medium amount', () {
136+
expect(
137+
midColor.adjust(alpha: -0.25),
138+
const Color(0x4040bfbf),
139+
);
140+
expect(
141+
midColor.adjust(alpha: 0.25),
142+
const Color(0xc040bfbf),
143+
);
144+
expect(
145+
midColor.adjust(hue: -90),
146+
const Color(0x8080bf40),
147+
);
148+
expect(
149+
midColor.adjust(hue: 90),
150+
const Color(0x808040bf),
151+
);
152+
expect(
153+
midColor.adjust(saturation: -0.25),
154+
const Color(0x80609f9f),
155+
);
156+
expect(
157+
midColor.adjust(saturation: 0.25),
158+
const Color(0x8020dfdf),
159+
);
160+
expect(
161+
midColor.adjust(lightness: -0.25),
162+
const Color(0x80206060),
163+
);
164+
expect(
165+
midColor.adjust(lightness: 0.25),
166+
const Color(0x80a0dfdf),
167+
);
168+
});
169+
});
170+
171+
group('Color.copyWith() test -', () {
172+
test('With out of range amount', () {
173+
expect(() => midColor.copyWith(alpha: -1.1), throwsAssertionError);
174+
expect(() => midColor.copyWith(alpha: 1.1), throwsAssertionError);
175+
expect(() => midColor.copyWith(hue: -360.1), throwsAssertionError);
176+
expect(() => midColor.copyWith(hue: 360.1), throwsAssertionError);
177+
expect(() => midColor.copyWith(saturation: -1.1), throwsAssertionError);
178+
expect(() => midColor.copyWith(saturation: 1.1), throwsAssertionError);
179+
expect(() => midColor.copyWith(lightness: -1.1), throwsAssertionError);
180+
expect(() => midColor.copyWith(lightness: 1.1), throwsAssertionError);
181+
});
182+
test('With various amount', () {
183+
expect(
184+
midColor.copyWith(alpha: 0.25),
185+
const Color(0x4040bfbf),
186+
);
187+
expect(
188+
midColor.copyWith(hue: 90),
189+
const Color(0x8080bf40),
190+
);
191+
expect(
192+
midColor.copyWith(saturation: 0.75),
193+
const Color(0x8020dfdf),
194+
);
195+
expect(
196+
midColor.copyWith(lightness: 0.75),
197+
const Color(0x80a0dfdf),
198+
);
199+
});
200+
});
201+
}

0 commit comments

Comments
 (0)