Select Color: CMYK sliders, field below the sliders, Document first

- Editor modes RGB, HSB and CMYK; the saturation/brightness field and hue
  strip now sit below the sliders in every mode instead of being a mode.
- The panel opens on the Document tab.
- Built-in colour books: RAL CLASSIC and SIKKENS 4041 (Open Colour
  Systems Collection 2.0, CC BY-ND 4.0); CSS COLORS removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013rGmDUNr1hUf4zo1X3dak8
This commit is contained in:
Claude committed 2026-10-14 11:57:53 +00:00
1 parent 00b925b9fa
commit ecdead20c7
7 files changed
+2205 -323

No files matched your search

+6 -6
View File
@@ -3,11 +3,11 @@
AutoCAD `.acb` colour books built into the Select Color panel. Each file
carries its source and licence in its header comment.
| File | Book | Licence |
| File | Book | Source and licence |
|---|---|---|
| `ral-classic.acb` | RAL CLASSIC, 216 approximate sRGB values | CC BY-SA 4.0, adapted from Wikipedia, "List of RAL colours" |
| `css-colors.acb` | CSS COLORS, the 148 CSS named colours | Values from the W3C CSS Color Module Level 4 |
| `ral-classic.acb` | RAL CLASSIC, 216 approximate sRGB values | Adapted from Wikipedia, "List of RAL colours"; CC BY-SA 4.0 |
| `sikkens-4041.acb` | SIKKENS 4041, 1,624 colours | Open Colour Systems Collection 2.0, © 2015 dtp studio oldenburg (freieFarbe e.V.); CC BY-ND 4.0. Converted from the GIMP palette: same names, values and order, only the file format changed |
RAL is a registered trademark of RAL gGmbH. The RAL values are approximate
screen representations, not colour-binding references, and are not
affiliated with, endorsed by or licensed by RAL gGmbH.
Values are approximate screen representations, not colour-binding
references. RAL is a registered trademark of RAL gGmbH and Sikkens of
AkzoNobel; these files are not affiliated with or endorsed by either.
-202
View File
@@ -1,202 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The named colours of CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/#named-colors),
sorted by hue. Names and values as the W3C specification defines them.
-->
<colorBook>
<bookName>CSS COLORS</bookName>
<majorVersion>1</majorVersion>
<minorVersion>0</minorVersion>
<colorPage>
<colorEntry><colorName>mistyrose</colorName><RGB8><red>255</red><green>228</green><blue>225</blue></RGB8></colorEntry>
<colorEntry><colorName>red</colorName><RGB8><red>255</red><green>0</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>salmon</colorName><RGB8><red>250</red><green>128</green><blue>114</blue></RGB8></colorEntry>
<colorEntry><colorName>lightcoral</colorName><RGB8><red>240</red><green>128</green><blue>128</blue></RGB8></colorEntry>
<colorEntry><colorName>indianred</colorName><RGB8><red>205</red><green>92</green><blue>92</blue></RGB8></colorEntry>
<colorEntry><colorName>rosybrown</colorName><RGB8><red>188</red><green>143</green><blue>143</blue></RGB8></colorEntry>
<colorEntry><colorName>firebrick</colorName><RGB8><red>178</red><green>34</green><blue>34</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>brown</colorName><RGB8><red>165</red><green>42</green><blue>42</blue></RGB8></colorEntry>
<colorEntry><colorName>darkred</colorName><RGB8><red>139</red><green>0</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>maroon</colorName><RGB8><red>128</red><green>0</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>coral</colorName><RGB8><red>255</red><green>127</green><blue>80</blue></RGB8></colorEntry>
<colorEntry><colorName>lightsalmon</colorName><RGB8><red>255</red><green>160</green><blue>122</blue></RGB8></colorEntry>
<colorEntry><colorName>orangered</colorName><RGB8><red>255</red><green>69</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>tomato</colorName><RGB8><red>255</red><green>99</green><blue>71</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>darksalmon</colorName><RGB8><red>233</red><green>150</green><blue>122</blue></RGB8></colorEntry>
<colorEntry><colorName>sienna</colorName><RGB8><red>160</red><green>82</green><blue>45</blue></RGB8></colorEntry>
<colorEntry><colorName>bisque</colorName><RGB8><red>255</red><green>228</green><blue>196</blue></RGB8></colorEntry>
<colorEntry><colorName>blanchedalmond</colorName><RGB8><red>255</red><green>235</green><blue>205</blue></RGB8></colorEntry>
<colorEntry><colorName>darkorange</colorName><RGB8><red>255</red><green>140</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>navajowhite</colorName><RGB8><red>255</red><green>222</green><blue>173</blue></RGB8></colorEntry>
<colorEntry><colorName>papayawhip</colorName><RGB8><red>255</red><green>239</green><blue>213</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>peachpuff</colorName><RGB8><red>255</red><green>218</green><blue>185</blue></RGB8></colorEntry>
<colorEntry><colorName>antiquewhite</colorName><RGB8><red>250</red><green>235</green><blue>215</blue></RGB8></colorEntry>
<colorEntry><colorName>sandybrown</colorName><RGB8><red>244</red><green>164</green><blue>96</blue></RGB8></colorEntry>
<colorEntry><colorName>burlywood</colorName><RGB8><red>222</red><green>184</green><blue>135</blue></RGB8></colorEntry>
<colorEntry><colorName>chocolate</colorName><RGB8><red>210</red><green>105</green><blue>30</blue></RGB8></colorEntry>
<colorEntry><colorName>tan</colorName><RGB8><red>210</red><green>180</green><blue>140</blue></RGB8></colorEntry>
<colorEntry><colorName>peru</colorName><RGB8><red>205</red><green>133</green><blue>63</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>saddlebrown</colorName><RGB8><red>139</red><green>69</green><blue>19</blue></RGB8></colorEntry>
<colorEntry><colorName>cornsilk</colorName><RGB8><red>255</red><green>248</green><blue>220</blue></RGB8></colorEntry>
<colorEntry><colorName>gold</colorName><RGB8><red>255</red><green>215</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>moccasin</colorName><RGB8><red>255</red><green>228</green><blue>181</blue></RGB8></colorEntry>
<colorEntry><colorName>orange</colorName><RGB8><red>255</red><green>165</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>wheat</colorName><RGB8><red>245</red><green>222</green><blue>179</blue></RGB8></colorEntry>
<colorEntry><colorName>goldenrod</colorName><RGB8><red>218</red><green>165</green><blue>32</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>darkgoldenrod</colorName><RGB8><red>184</red><green>134</green><blue>11</blue></RGB8></colorEntry>
<colorEntry><colorName>lemonchiffon</colorName><RGB8><red>255</red><green>250</green><blue>205</blue></RGB8></colorEntry>
<colorEntry><colorName>lightyellow</colorName><RGB8><red>255</red><green>255</green><blue>224</blue></RGB8></colorEntry>
<colorEntry><colorName>yellow</colorName><RGB8><red>255</red><green>255</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>lightgoldenrodyellow</colorName><RGB8><red>250</red><green>250</green><blue>210</blue></RGB8></colorEntry>
<colorEntry><colorName>beige</colorName><RGB8><red>245</red><green>245</green><blue>220</blue></RGB8></colorEntry>
<colorEntry><colorName>khaki</colorName><RGB8><red>240</red><green>230</green><blue>140</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>palegoldenrod</colorName><RGB8><red>238</red><green>232</green><blue>170</blue></RGB8></colorEntry>
<colorEntry><colorName>darkkhaki</colorName><RGB8><red>189</red><green>183</green><blue>107</blue></RGB8></colorEntry>
<colorEntry><colorName>olive</colorName><RGB8><red>128</red><green>128</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>yellowgreen</colorName><RGB8><red>154</red><green>205</green><blue>50</blue></RGB8></colorEntry>
<colorEntry><colorName>olivedrab</colorName><RGB8><red>107</red><green>142</green><blue>35</blue></RGB8></colorEntry>
<colorEntry><colorName>darkolivegreen</colorName><RGB8><red>85</red><green>107</green><blue>47</blue></RGB8></colorEntry>
<colorEntry><colorName>chartreuse</colorName><RGB8><red>127</red><green>255</green><blue>0</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>greenyellow</colorName><RGB8><red>173</red><green>255</green><blue>47</blue></RGB8></colorEntry>
<colorEntry><colorName>lawngreen</colorName><RGB8><red>124</red><green>252</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>lime</colorName><RGB8><red>0</red><green>255</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>palegreen</colorName><RGB8><red>152</red><green>251</green><blue>152</blue></RGB8></colorEntry>
<colorEntry><colorName>lightgreen</colorName><RGB8><red>144</red><green>238</green><blue>144</blue></RGB8></colorEntry>
<colorEntry><colorName>limegreen</colorName><RGB8><red>50</red><green>205</green><blue>50</blue></RGB8></colorEntry>
<colorEntry><colorName>darkseagreen</colorName><RGB8><red>143</red><green>188</green><blue>143</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>forestgreen</colorName><RGB8><red>34</red><green>139</green><blue>34</blue></RGB8></colorEntry>
<colorEntry><colorName>green</colorName><RGB8><red>0</red><green>128</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>darkgreen</colorName><RGB8><red>0</red><green>100</green><blue>0</blue></RGB8></colorEntry>
<colorEntry><colorName>springgreen</colorName><RGB8><red>0</red><green>255</green><blue>127</blue></RGB8></colorEntry>
<colorEntry><colorName>mediumspringgreen</colorName><RGB8><red>0</red><green>250</green><blue>154</blue></RGB8></colorEntry>
<colorEntry><colorName>mediumseagreen</colorName><RGB8><red>60</red><green>179</green><blue>113</blue></RGB8></colorEntry>
<colorEntry><colorName>seagreen</colorName><RGB8><red>46</red><green>139</green><blue>87</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>aquamarine</colorName><RGB8><red>127</red><green>255</green><blue>212</blue></RGB8></colorEntry>
<colorEntry><colorName>mediumaquamarine</colorName><RGB8><red>102</red><green>205</green><blue>170</blue></RGB8></colorEntry>
<colorEntry><colorName>aqua</colorName><RGB8><red>0</red><green>255</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>cyan</colorName><RGB8><red>0</red><green>255</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>lightcyan</colorName><RGB8><red>224</red><green>255</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>paleturquoise</colorName><RGB8><red>175</red><green>238</green><blue>238</blue></RGB8></colorEntry>
<colorEntry><colorName>powderblue</colorName><RGB8><red>176</red><green>224</green><blue>230</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>turquoise</colorName><RGB8><red>64</red><green>224</green><blue>208</blue></RGB8></colorEntry>
<colorEntry><colorName>darkturquoise</colorName><RGB8><red>0</red><green>206</green><blue>209</blue></RGB8></colorEntry>
<colorEntry><colorName>mediumturquoise</colorName><RGB8><red>72</red><green>209</green><blue>204</blue></RGB8></colorEntry>
<colorEntry><colorName>lightseagreen</colorName><RGB8><red>32</red><green>178</green><blue>170</blue></RGB8></colorEntry>
<colorEntry><colorName>cadetblue</colorName><RGB8><red>95</red><green>158</green><blue>160</blue></RGB8></colorEntry>
<colorEntry><colorName>darkcyan</colorName><RGB8><red>0</red><green>139</green><blue>139</blue></RGB8></colorEntry>
<colorEntry><colorName>teal</colorName><RGB8><red>0</red><green>128</green><blue>128</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>darkslategray</colorName><RGB8><red>47</red><green>79</green><blue>79</blue></RGB8></colorEntry>
<colorEntry><colorName>darkslategrey</colorName><RGB8><red>47</red><green>79</green><blue>79</blue></RGB8></colorEntry>
<colorEntry><colorName>deepskyblue</colorName><RGB8><red>0</red><green>191</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>skyblue</colorName><RGB8><red>135</red><green>206</green><blue>235</blue></RGB8></colorEntry>
<colorEntry><colorName>lightblue</colorName><RGB8><red>173</red><green>216</green><blue>230</blue></RGB8></colorEntry>
<colorEntry><colorName>dodgerblue</colorName><RGB8><red>30</red><green>144</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>lightskyblue</colorName><RGB8><red>135</red><green>206</green><blue>250</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>lightsteelblue</colorName><RGB8><red>176</red><green>196</green><blue>222</blue></RGB8></colorEntry>
<colorEntry><colorName>steelblue</colorName><RGB8><red>70</red><green>130</green><blue>180</blue></RGB8></colorEntry>
<colorEntry><colorName>lightslategray</colorName><RGB8><red>119</red><green>136</green><blue>153</blue></RGB8></colorEntry>
<colorEntry><colorName>lightslategrey</colorName><RGB8><red>119</red><green>136</green><blue>153</blue></RGB8></colorEntry>
<colorEntry><colorName>slategray</colorName><RGB8><red>112</red><green>128</green><blue>144</blue></RGB8></colorEntry>
<colorEntry><colorName>slategrey</colorName><RGB8><red>112</red><green>128</green><blue>144</blue></RGB8></colorEntry>
<colorEntry><colorName>cornflowerblue</colorName><RGB8><red>100</red><green>149</green><blue>237</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>royalblue</colorName><RGB8><red>65</red><green>105</green><blue>225</blue></RGB8></colorEntry>
<colorEntry><colorName>blue</colorName><RGB8><red>0</red><green>0</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>mediumblue</colorName><RGB8><red>0</red><green>0</green><blue>205</blue></RGB8></colorEntry>
<colorEntry><colorName>darkblue</colorName><RGB8><red>0</red><green>0</green><blue>139</blue></RGB8></colorEntry>
<colorEntry><colorName>navy</colorName><RGB8><red>0</red><green>0</green><blue>128</blue></RGB8></colorEntry>
<colorEntry><colorName>midnightblue</colorName><RGB8><red>25</red><green>25</green><blue>112</blue></RGB8></colorEntry>
<colorEntry><colorName>mediumslateblue</colorName><RGB8><red>123</red><green>104</green><blue>238</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>mediumpurple</colorName><RGB8><red>147</red><green>112</green><blue>219</blue></RGB8></colorEntry>
<colorEntry><colorName>slateblue</colorName><RGB8><red>106</red><green>90</green><blue>205</blue></RGB8></colorEntry>
<colorEntry><colorName>darkslateblue</colorName><RGB8><red>72</red><green>61</green><blue>139</blue></RGB8></colorEntry>
<colorEntry><colorName>blueviolet</colorName><RGB8><red>138</red><green>43</green><blue>226</blue></RGB8></colorEntry>
<colorEntry><colorName>rebeccapurple</colorName><RGB8><red>102</red><green>51</green><blue>153</blue></RGB8></colorEntry>
<colorEntry><colorName>indigo</colorName><RGB8><red>75</red><green>0</green><blue>130</blue></RGB8></colorEntry>
<colorEntry><colorName>darkviolet</colorName><RGB8><red>148</red><green>0</green><blue>211</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>mediumorchid</colorName><RGB8><red>186</red><green>85</green><blue>211</blue></RGB8></colorEntry>
<colorEntry><colorName>darkorchid</colorName><RGB8><red>153</red><green>50</green><blue>204</blue></RGB8></colorEntry>
<colorEntry><colorName>fuchsia</colorName><RGB8><red>255</red><green>0</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>magenta</colorName><RGB8><red>255</red><green>0</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>violet</colorName><RGB8><red>238</red><green>130</green><blue>238</blue></RGB8></colorEntry>
<colorEntry><colorName>plum</colorName><RGB8><red>221</red><green>160</green><blue>221</blue></RGB8></colorEntry>
<colorEntry><colorName>orchid</colorName><RGB8><red>218</red><green>112</green><blue>214</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>thistle</colorName><RGB8><red>216</red><green>191</green><blue>216</blue></RGB8></colorEntry>
<colorEntry><colorName>darkmagenta</colorName><RGB8><red>139</red><green>0</green><blue>139</blue></RGB8></colorEntry>
<colorEntry><colorName>purple</colorName><RGB8><red>128</red><green>0</green><blue>128</blue></RGB8></colorEntry>
<colorEntry><colorName>mediumvioletred</colorName><RGB8><red>199</red><green>21</green><blue>133</blue></RGB8></colorEntry>
<colorEntry><colorName>deeppink</colorName><RGB8><red>255</red><green>20</green><blue>147</blue></RGB8></colorEntry>
<colorEntry><colorName>hotpink</colorName><RGB8><red>255</red><green>105</green><blue>180</blue></RGB8></colorEntry>
<colorEntry><colorName>lightpink</colorName><RGB8><red>255</red><green>182</green><blue>193</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>pink</colorName><RGB8><red>255</red><green>192</green><blue>203</blue></RGB8></colorEntry>
<colorEntry><colorName>crimson</colorName><RGB8><red>220</red><green>20</green><blue>60</blue></RGB8></colorEntry>
<colorEntry><colorName>palevioletred</colorName><RGB8><red>219</red><green>112</green><blue>147</blue></RGB8></colorEntry>
<colorEntry><colorName>aliceblue</colorName><RGB8><red>240</red><green>248</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>azure</colorName><RGB8><red>240</red><green>255</green><blue>255</blue></RGB8></colorEntry>
<colorEntry><colorName>floralwhite</colorName><RGB8><red>255</red><green>250</green><blue>240</blue></RGB8></colorEntry>
<colorEntry><colorName>ghostwhite</colorName><RGB8><red>248</red><green>248</green><blue>255</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>honeydew</colorName><RGB8><red>240</red><green>255</green><blue>240</blue></RGB8></colorEntry>
<colorEntry><colorName>ivory</colorName><RGB8><red>255</red><green>255</green><blue>240</blue></RGB8></colorEntry>
<colorEntry><colorName>lavenderblush</colorName><RGB8><red>255</red><green>240</green><blue>245</blue></RGB8></colorEntry>
<colorEntry><colorName>mintcream</colorName><RGB8><red>245</red><green>255</green><blue>250</blue></RGB8></colorEntry>
<colorEntry><colorName>seashell</colorName><RGB8><red>255</red><green>245</green><blue>238</blue></RGB8></colorEntry>
<colorEntry><colorName>snow</colorName><RGB8><red>255</red><green>250</green><blue>250</blue></RGB8></colorEntry>
<colorEntry><colorName>white</colorName><RGB8><red>255</red><green>255</green><blue>255</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>oldlace</colorName><RGB8><red>253</red><green>245</green><blue>230</blue></RGB8></colorEntry>
<colorEntry><colorName>lavender</colorName><RGB8><red>230</red><green>230</green><blue>250</blue></RGB8></colorEntry>
<colorEntry><colorName>linen</colorName><RGB8><red>250</red><green>240</green><blue>230</blue></RGB8></colorEntry>
<colorEntry><colorName>whitesmoke</colorName><RGB8><red>245</red><green>245</green><blue>245</blue></RGB8></colorEntry>
<colorEntry><colorName>gainsboro</colorName><RGB8><red>220</red><green>220</green><blue>220</blue></RGB8></colorEntry>
<colorEntry><colorName>lightgray</colorName><RGB8><red>211</red><green>211</green><blue>211</blue></RGB8></colorEntry>
<colorEntry><colorName>lightgrey</colorName><RGB8><red>211</red><green>211</green><blue>211</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>silver</colorName><RGB8><red>192</red><green>192</green><blue>192</blue></RGB8></colorEntry>
<colorEntry><colorName>darkgray</colorName><RGB8><red>169</red><green>169</green><blue>169</blue></RGB8></colorEntry>
<colorEntry><colorName>darkgrey</colorName><RGB8><red>169</red><green>169</green><blue>169</blue></RGB8></colorEntry>
<colorEntry><colorName>gray</colorName><RGB8><red>128</red><green>128</green><blue>128</blue></RGB8></colorEntry>
<colorEntry><colorName>grey</colorName><RGB8><red>128</red><green>128</green><blue>128</blue></RGB8></colorEntry>
<colorEntry><colorName>dimgray</colorName><RGB8><red>105</red><green>105</green><blue>105</blue></RGB8></colorEntry>
<colorEntry><colorName>dimgrey</colorName><RGB8><red>105</red><green>105</green><blue>105</blue></RGB8></colorEntry>
</colorPage>
<colorPage>
<colorEntry><colorName>black</colorName><RGB8><red>0</red><green>0</green><blue>0</blue></RGB8></colorEntry>
</colorPage>
</colorBook>
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -62,7 +62,7 @@ impl ColorBook {
pub fn builtin() -> impl Iterator<Item = ColorBook> {
[
include_str!("../../../assets/color_books/ral-classic.acb"),
include_str!("../../../assets/color_books/css-colors.acb"),
include_str!("../../../assets/color_books/sikkens-4041.acb"),
]
.into_iter()
.filter_map(|text| parse(text).ok())
+67 -94
View File
@@ -1,6 +1,6 @@
//! The colour editor: HSB or RGB sliders, or a saturation/brightness field
//! with a hue strip, plus a hex box. Shared by the CAD Select Color panel and
//! the interface colour picker.
//! The colour editor: RGB, HSB or CMYK sliders above a saturation/brightness
//! field with a hue strip, plus a hex box. Shared by the CAD Select Color
//! panel and the interface colour picker.
use super::math::{self, Hsb};
use super::widgets::ColorArea;
@@ -15,19 +15,17 @@ use iced::{Background, Border, Color, Element, Gradient, Length, Radians, Theme}
const ROW_HEIGHT: f32 = 26.0;
const TRACK: f32 = 18.0;
const STRIP_WIDTH: f32 = 22.0;
/// Gap between slider rows. Together with `FIELD_HEIGHT` it puts the hex row
/// at the same height in every mode: 3 × 26 + 2 × 14 + 12 = 108 + 10.
const ROW_GAP: f32 = 14.0;
const FIELD_HEIGHT: f32 = 108.0;
const MODE_WIDTH: f32 = 40.0;
const ROW_GAP: f32 = 8.0;
const FIELD_HEIGHT: f32 = 96.0;
const MODE_WIDTH: f32 = 48.0;
const MODE_HEIGHT: f32 = 24.0;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum EditorMode {
Rgb,
#[default]
Hsb,
Rgb,
Field,
Cmyk,
}
#[derive(Debug, Clone)]
@@ -43,6 +41,8 @@ pub enum EditorMsg {
Red(u8),
Green(u8),
Blue(u8),
/// One CMYK channel (0 cyan … 3 black), in percent.
Cmyk(usize, f32),
HexInput(String),
HexSubmit,
}
@@ -51,6 +51,8 @@ pub struct ColorEditor {
mode: EditorMode,
/// Kept beside `rgb` so hue and saturation survive grays and black.
hsb: Hsb,
/// Kept too, so black keeps its inks and rounding doesn't move them.
cmyk: [f32; 4],
rgb: (u8, u8, u8),
hex_draft: Option<String>,
}
@@ -64,6 +66,7 @@ impl ColorEditor {
s: 0.0,
b: 1.0,
},
cmyk: [0.0; 4],
rgb,
hex_draft: None,
};
@@ -85,6 +88,7 @@ impl ColorEditor {
s: if next.b == 0.0 { self.hsb.s } else { next.s },
b: next.b,
};
self.cmyk = math::rgb_to_cmyk(r, g, b);
self.rgb = (r, g, b);
self.hex_draft = None;
}
@@ -96,9 +100,17 @@ impl ColorEditor {
b: hsb.b.clamp(0.0, 1.0),
};
self.rgb = self.hsb.to_rgb();
self.cmyk = math::rgb_to_cmyk(self.rgb.0, self.rgb.1, self.rgb.2);
self.hex_draft = None;
}
fn set_cmyk(&mut self, channel: usize, value: f32) {
let mut cmyk = self.cmyk;
cmyk[channel.min(3)] = value.clamp(0.0, 100.0);
self.set_rgb(math::cmyk_to_rgb(cmyk));
self.cmyk = cmyk;
}
/// Apply a message; returns whether the colour changed.
pub fn update(&mut self, msg: EditorMsg) -> bool {
let before = self.rgb;
@@ -118,6 +130,7 @@ impl ColorEditor {
EditorMsg::Red(v) => self.set_rgb((v, g, b)),
EditorMsg::Green(v) => self.set_rgb((r, v, b)),
EditorMsg::Blue(v) => self.set_rgb((r, g, v)),
EditorMsg::Cmyk(channel, v) => self.set_cmyk(channel, v),
EditorMsg::HexInput(value) => {
// Six digits are unambiguous, so apply them while typing; the
// short #RGB form waits for Enter.
@@ -138,28 +151,26 @@ impl ColorEditor {
/// `mode_end` sits at the far right of the mode buttons (the pipette);
/// `hex_end` at the far right of the hex row (the nearest index colour).
/// The field view shows the hex value beside H, S and B and leaves out
/// `hex_end`; the slider views end on a hex row.
pub fn view<'a>(
&'a self,
mode_end: Option<Element<'a, Message>>,
hex_end: Option<Element<'a, Message>>,
) -> Element<'a, Message> {
let view = column![self.mode_row(mode_end)].spacing(12);
match self.mode {
EditorMode::Hsb | EditorMode::Rgb => view
.push(self.slider_rows())
.push(self.hex_row(hex_end))
.into(),
EditorMode::Field => view.push(self.field_view()).into(),
}
column![
self.mode_row(mode_end),
self.slider_rows(),
self.field(),
self.hex_row(hex_end),
]
.spacing(12)
.into()
}
/// HSB, RGB and the gradient-field button, with `end` at the far right.
/// RGB, HSB and CMYK, with `end` at the far right.
fn mode_row<'a>(&self, end: Option<Element<'a, Message>>) -> Element<'a, Message> {
let mode_button = |content: Element<'a, Message>, mode: EditorMode| {
let mode_button = |label: &'static str, mode: EditorMode| {
let active = self.mode == mode;
button(container(content).center(Length::Fill))
button(container(text(label).size(13)).center(Length::Fill))
.on_press(msg(EditorMsg::Mode(mode)))
.width(MODE_WIDTH)
.height(MODE_HEIGHT)
@@ -168,35 +179,10 @@ impl ColorEditor {
super::picker::toggle_style(theme, active, status)
})
};
// The field button is only its gradient, without a frame; it takes
// colour when its mode is active and stays gray otherwise.
let field_active = self.mode == EditorMode::Field;
let field_button = button(Space::new().width(MODE_WIDTH).height(MODE_HEIGHT))
.on_press(msg(EditorMsg::Mode(EditorMode::Field)))
.padding(0)
.style(move |_theme: &Theme, _status| {
let middle = if field_active {
Color::from_rgb8(0x3A, 0xA0, 0xFF)
} else {
Color::from_rgb8(0x80, 0x80, 0x80)
};
let icon = gradient_box(
3.0 * std::f32::consts::FRAC_PI_4,
&[(0.0, Color::WHITE), (0.5, middle), (1.0, Color::BLACK)],
);
button::Style {
background: icon.background,
border: Border {
radius: 5.0.into(),
..Border::default()
},
..Default::default()
}
});
let mut modes = row![
mode_button(text("HSB").size(13).into(), EditorMode::Hsb),
mode_button(text("RGB").size(13).into(), EditorMode::Rgb),
field_button,
mode_button("RGB", EditorMode::Rgb),
mode_button("HSB", EditorMode::Hsb),
mode_button("CMYK", EditorMode::Cmyk),
Space::new().width(Length::Fill),
]
.spacing(6);
@@ -206,12 +192,29 @@ impl ColorEditor {
modes.into()
}
/// Three gradient sliders: hue, saturation and brightness, or red, green
/// and blue.
/// Gradient sliders: red, green and blue; hue, saturation and
/// brightness; or cyan, magenta, yellow and black.
fn slider_rows<'a>(&self) -> Element<'a, Message> {
let Hsb { h, s, b } = self.hsb;
let (r, g, bl) = self.rgb;
let rows = if self.mode == EditorMode::Rgb {
let rows = if self.mode == EditorMode::Cmyk {
let ink = |i: usize, letter| {
let at = |v| {
let mut cmyk = self.cmyk;
cmyk[i] = v;
rgb_color(math::cmyk_to_rgb(cmyk))
};
channel(
letter,
self.cmyk[i],
100.0,
Some('%'),
vec![(0.0, at(0.0)), (1.0, at(100.0))],
move |v| EditorMsg::Cmyk(i, v),
)
};
column![ink(0, "C"), ink(1, "M"), ink(2, "Y"), ink(3, "K")]
} else if self.mode == EditorMode::Rgb {
let ends = |lo: (u8, u8, u8), hi: (u8, u8, u8)| {
vec![(0.0, rgb_color(lo)), (1.0, rgb_color(hi))]
};
@@ -266,9 +269,8 @@ impl ColorEditor {
rows.spacing(ROW_GAP).into()
}
/// The saturation/brightness field with its hue strip, and below it H, S
/// and B on the left and the hex value on the right.
fn field_view<'a>(&self) -> Element<'a, Message> {
/// The saturation/brightness field with its hue strip.
fn field<'a>(&self) -> Element<'a, Message> {
let Hsb { h, s, b } = self.hsb;
let area = ColorArea::new(hsb_color(h, 1.0, 1.0), rgb_color(self.rgb), s, b, |s, b| {
msg(EditorMsg::Field(s, b))
@@ -285,50 +287,17 @@ impl ColorEditor {
.height(FIELD_HEIGHT)
.style(handle_style),
];
let value =
|letter: &'static str, v: f32, max: f32, unit: char, f: fn(f32) -> EditorMsg| {
row![
text(letter).size(12).font(iced::Font::MONOSPACE),
number_box(v, max, Some(unit), f),
]
.spacing(4)
.align_y(iced::Center)
};
column![
row![area, strip].spacing(12),
row![
value("H", h, 360.0, '°', EditorMsg::Hue),
value("S", s * 100.0, 100.0, '%', EditorMsg::Saturation),
value("B", b * 100.0, 100.0, '%', EditorMsg::Brightness),
Space::new().width(Length::Fill),
self.hex_field(),
]
.spacing(10)
.align_y(iced::Center),
]
// 108 + 10 matches the slider views' 3 × 26 + 2 × 14 + 12.
.spacing(10)
.into()
row![area, strip].spacing(12).into()
}
/// The hex value with `end` (the nearest index colour) at the far right.
fn hex_row<'a>(&self, end: Option<Element<'a, Message>>) -> Element<'a, Message> {
let mut hex_row = row![self.hex_field(), Space::new().width(Length::Fill)]
.spacing(8)
.align_y(iced::Center);
if let Some(end) = end {
hex_row = hex_row.push(end);
}
hex_row.into()
}
fn hex_field<'a>(&self) -> Element<'a, Message> {
let (r, g, b) = self.rgb;
let hex = self
.hex_draft
.clone()
.unwrap_or_else(|| math::hex_string(r, g, b));
row![
let mut hex_row = row![
text(t!("Hex")).size(11).style(super::picker::muted_text),
text_input("", &hex)
.on_input(|value| msg(EditorMsg::HexInput(value)))
@@ -337,10 +306,14 @@ impl ColorEditor {
.font(iced::Font::MONOSPACE)
.padding([5, 6])
.width(Length::Fixed(74.0)),
Space::new().width(Length::Fill),
]
.spacing(8)
.align_y(iced::Center)
.into()
.align_y(iced::Center);
if let Some(end) = end {
hex_row = hex_row.push(end);
}
hex_row.into()
}
}
+18
View File
@@ -59,6 +59,24 @@ pub fn rgb_to_hsb(r: u8, g: u8, b: u8) -> Hsb {
Hsb { h, s, b: max }
}
/// Cyan, magenta, yellow and black in percent, by the plain device-free
/// formula (no ICC profile): screen values, not press values.
pub fn rgb_to_cmyk(r: u8, g: u8, b: u8) -> [f32; 4] {
let max = r.max(g).max(b);
if max == 0 {
return [0.0, 0.0, 0.0, 100.0];
}
let max = f32::from(max);
let ink = |v: u8| (1.0 - f32::from(v) / max) * 100.0;
[ink(r), ink(g), ink(b), (1.0 - max / 255.0) * 100.0]
}
pub fn cmyk_to_rgb([c, m, y, k]: [f32; 4]) -> (u8, u8, u8) {
let white = 255.0 * (1.0 - k.clamp(0.0, 100.0) / 100.0);
let channel = |ink: f32| (white * (1.0 - ink.clamp(0.0, 100.0) / 100.0)).round() as u8;
(channel(c), channel(m), channel(y))
}
/// RGB of an ACI entry, or `None` outside 1–255.
pub fn aci_rgb(index: u8) -> Option<(u8, u8, u8)> {
codec::types::aci_table::aci_to_rgb(index)
+12 -20
View File
@@ -24,7 +24,7 @@ const PANEL_WIDTH: f32 = 400.0;
/// Scope for Tab / Shift+Tab, so focus cycles through the panel's own
/// fields instead of the dialogs and command line underneath.
const PANEL_ID: &str = "select-color-panel";
const BODY_HEIGHT: f32 = 352.0;
const BODY_HEIGHT: f32 = 368.0;
/// Swatches per row in the Document tab.
const DOC_COLUMNS: usize = 8;
/// Search matches shown at once in the Color books tab.
@@ -32,8 +32,8 @@ const PAGE_BARS: usize = 7;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ColorPickerTab {
Document,
#[default]
Document,
Index,
TrueColor,
Books,
@@ -106,11 +106,6 @@ pub struct ColorPicker {
impl ColorPicker {
pub fn new(target: ColorPickTarget, current: AcadColor, varies: bool) -> Self {
let tab = if matches!(current, AcadColor::Rgb { .. }) {
ColorPickerTab::TrueColor
} else {
ColorPickerTab::Index
};
Self {
target,
current,
@@ -118,7 +113,7 @@ impl ColorPicker {
pending: current,
current_name: None,
pending_name: None,
tab,
tab: ColorPickerTab::Document,
editor: ColorEditor::new(math::color_rgb(current).unwrap_or((255, 255, 255))),
eyedropper: None,
doc: Vec::new(),
@@ -133,8 +128,7 @@ impl ColorPicker {
}
/// Fill the Document and Color books tabs. `current_name` is the
/// `BOOK$NAME` of the current colour; a colour from a loaded book opens
/// on its page.
/// `BOOK$NAME` of the current colour; the Color books tab shows its page.
pub fn with_drawing(
mut self,
mut doc: Vec<DocColor>,
@@ -147,9 +141,7 @@ impl ColorPicker {
self.recent = recent.to_vec();
self.books = books;
if let Some(name) = current_name.filter(|name| !name.is_empty()) {
if self.show_identity(&name) {
self.tab = ColorPickerTab::Books;
}
self.show_identity(&name);
self.current_name = Some(name.clone());
self.pending_name = Some(name);
}
@@ -1152,13 +1144,12 @@ mod tests {
}
#[test]
fn opens_on_the_tab_that_fits_the_current_colour() {
assert_eq!(picker(AcadColor::Index(30)).tab(), ColorPickerTab::Index);
fn opens_on_the_document_tab() {
assert_eq!(picker(AcadColor::Index(30)).tab(), ColorPickerTab::Document);
assert_eq!(
picker(AcadColor::Rgb { r: 1, g: 2, b: 3 }).tab(),
ColorPickerTab::TrueColor
ColorPickerTab::Document
);
assert_eq!(picker(AcadColor::ByLayer).tab(), ColorPickerTab::Index);
}
#[test]
@@ -1225,6 +1216,7 @@ mod tests {
assert_eq!(step_index(255, ArrowKey::Right), None);
let mut p = picker(AcadColor::Index(140));
p.update(ColorPickerMsg::Tab(ColorPickerTab::Index));
p.update(ColorPickerMsg::Arrow(ArrowKey::Right));
assert_eq!(p.pending(), AcadColor::Index(150));
}
@@ -1313,7 +1305,6 @@ mod tests {
b: 179,
})
.with_drawing(Vec::new(), &[], ral(), Some("RAL CLASSIC$RAL 5015".into()));
assert_eq!(p.tab(), ColorPickerTab::Books);
assert_eq!(p.book_selection(), Some(2));
assert_eq!(p.book_page, 1);
}
@@ -1388,6 +1379,7 @@ mod tests {
g: 191,
b: 252,
});
p.update(ColorPickerMsg::Tab(ColorPickerTab::TrueColor));
edit(&mut p, EditorMsg::Mode(EditorMode::Rgb));
let mut ui = simulator(&p);
let label = ui.find("R").expect("R label");
@@ -1489,11 +1481,11 @@ mod tests {
false,
),
(
"field",
"cmyk",
ColorPickTarget::Properties,
blue,
ColorPickerTab::TrueColor,
EditorMode::Field,
EditorMode::Cmyk,
false,
),
(