1 /++ 2 Displays a color-picker dialog box. 3 +/ 4 module arsd.minigui_addons.color_dialog; 5 6 import arsd.minigui; 7 8 static if(UsingWin32Widgets) 9 pragma(lib, "comdlg32"); 10 11 /++ 12 13 +/ 14 auto showColorDialog(Window owner, Color current, void delegate(Color choice) onOK, void delegate() onCancel = null) { 15 static if(UsingWin32Widgets) { 16 import core.sys.windows.windows; 17 static COLORREF[16] customColors; 18 CHOOSECOLOR cc; 19 cc.lStructSize = cc.sizeof; 20 cc.hwndOwner = owner ? owner.win.impl.hwnd : null; 21 cc.lpCustColors = cast(LPDWORD) customColors.ptr; 22 cc.rgbResult = RGB(current.r, current.g, current.b); 23 cc.Flags = CC_FULLOPEN | CC_RGBINIT; 24 if(ChooseColor(&cc)) { 25 onOK(Color(GetRValue(cc.rgbResult), GetGValue(cc.rgbResult), GetBValue(cc.rgbResult))); 26 } else { 27 if(onCancel) 28 onCancel(); 29 } 30 } else static if(UsingCustomWidgets) { 31 auto cpd = new ColorPickerDialog(current, onOK, owner); 32 cpd.show(); 33 return cpd; 34 } else static assert(0); 35 } 36 37 /* 38 Hue / Saturation picker 39 Lightness Picker 40 41 Text selections 42 43 Graphical representation 44 45 Cancel OK 46 */ 47 48 static if(UsingCustomWidgets) 49 class ColorPickerDialog : Dialog { 50 static arsd.simpledisplay.Sprite hslImage; 51 52 static bool canUseImage; 53 54 void delegate(Color) onOK; 55 56 this(Color current, void delegate(Color) onOK, Window owner) { 57 super(360, 460, "Color picker"); 58 59 this.onOK = onOK; 60 61 62 /* 63 statusBar.parts ~= new StatusBar.Part(140); 64 statusBar.parts ~= new StatusBar.Part(140); 65 statusBar.parts ~= new StatusBar.Part(140); 66 statusBar.parts ~= new StatusBar.Part(140); 67 this.addEventListener("mouseover", (Event ev) { 68 import std.conv; 69 this.statusBar.parts[2].content = to!string(ev.target.minHeight) ~ " - " ~ to!string(ev.target.maxHeight); 70 this.statusBar.parts[3].content = ev.target.toString(); 71 }); 72 */ 73 74 75 static if(UsingSimpledisplayX11) 76 // it is brutally slow over the network if we don't 77 // have xshm, so we've gotta do something else. 78 canUseImage = Image.impl.xshmAvailable; 79 else 80 canUseImage = true; 81 82 if(hslImage is null && canUseImage) { 83 auto img = new TrueColorImage(360, 255); 84 double h = 0.0, s = 1.0, l = 0.5; 85 foreach(y; 0 .. img.height) { 86 foreach(x; 0 .. img.width) { 87 img.imageData.colors[y * img.width + x] = Color.fromHsl(h,s,l); 88 h += 360.0 / img.width; 89 } 90 h = 0.0; 91 s -= 1.0 / img.height; 92 } 93 94 hslImage = new arsd.simpledisplay.Sprite(this.win, Image.fromMemoryImage(img)); 95 } 96 97 auto t = this; 98 99 auto wid = new class Widget { 100 this() { super(t); } 101 override int minHeight() { return hslImage ? hslImage.height : 4; } 102 override int maxHeight() { return hslImage ? hslImage.height : 4; } 103 override int marginBottom() { return 4; } 104 override void paint(WidgetPainter painter) { 105 if(hslImage) 106 hslImage.drawAt(painter, Point(0, 0)); 107 } 108 }; 109 110 auto hr = new HorizontalLayout(t); 111 112 auto vlRgb = new class VerticalLayout { 113 this() { 114 super(hr); 115 } 116 override int maxWidth() { return 150; }; 117 }; 118 119 auto vlHsl = new class VerticalLayout { 120 this() { 121 super(hr); 122 } 123 override int maxWidth() { return 150; }; 124 }; 125 126 h = new LabeledLineEdit("Hue:", TextAlignment.Right, vlHsl); 127 s = new LabeledLineEdit("Saturation:", TextAlignment.Right, vlHsl); 128 l = new LabeledLineEdit("Lightness:", TextAlignment.Right, vlHsl); 129 130 css = new LabeledLineEdit("CSS:", TextAlignment.Right, vlHsl); 131 132 r = new LabeledLineEdit("Red:", TextAlignment.Right, vlRgb); 133 g = new LabeledLineEdit("Green:", TextAlignment.Right, vlRgb); 134 b = new LabeledLineEdit("Blue:", TextAlignment.Right, vlRgb); 135 a = new LabeledLineEdit("Alpha:", TextAlignment.Right, vlRgb); 136 137 import std.conv; 138 import std.format; 139 140 void updateCurrent() { 141 r.content = to!string(current.r); 142 g.content = to!string(current.g); 143 b.content = to!string(current.b); 144 a.content = to!string(current.a); 145 146 auto hsl = current.toHsl; 147 148 h.content = format("%0.2f", hsl[0]); 149 s.content = format("%0.2f", hsl[1]); 150 l.content = format("%0.2f", hsl[2]); 151 152 css.content = current.toCssString(); 153 } 154 155 updateCurrent(); 156 157 r.addEventListener("focus", &r.selectAll); 158 g.addEventListener("focus", &g.selectAll); 159 b.addEventListener("focus", &b.selectAll); 160 a.addEventListener("focus", &a.selectAll); 161 162 h.addEventListener("focus", &h.selectAll); 163 s.addEventListener("focus", &s.selectAll); 164 l.addEventListener("focus", &l.selectAll); 165 166 css.addEventListener("focus", &css.selectAll); 167 168 void convertFromHsl() { 169 try { 170 auto c = Color.fromHsl(h.content.to!double, s.content.to!double, l.content.to!double); 171 c.a = a.content.to!ubyte; 172 current = c; 173 updateCurrent(); 174 } catch(Exception e) { 175 } 176 } 177 178 h.addEventListener("change", &convertFromHsl); 179 s.addEventListener("change", &convertFromHsl); 180 l.addEventListener("change", &convertFromHsl); 181 182 css.addEventListener("change", () { 183 current = Color.fromString(css.content); 184 updateCurrent(); 185 }); 186 187 void helper(MouseEventBase event) { 188 auto h = cast(double) event.clientX / hslImage.width * 360.0; 189 auto s = 1.0 - (cast(double) event.clientY / hslImage.height * 1.0); 190 auto l = this.l.content.to!double; 191 192 current = Color.fromHsl(h, s, l); 193 current.a = a.content.to!ubyte; 194 195 updateCurrent(); 196 197 auto e2 = new Event("change", this); 198 e2.dispatch(); 199 } 200 201 if(hslImage !is null) 202 wid.addEventListener((MouseDownEvent ev) { helper(ev); }); 203 if(hslImage !is null) 204 wid.addEventListener((MouseMoveEvent event) { 205 if(event.state & ModifierState.leftButtonDown) 206 helper(event); 207 }); 208 209 this.addEventListener((KeyDownEvent event) { 210 if(event.key == Key.Enter || event.key == Key.PadEnter) 211 OK(); 212 if(event.key == Key.Escape) 213 Cancel(); 214 }); 215 216 this.addEventListener("change", { 217 redraw(); 218 }); 219 220 auto s = this; 221 auto currentColorWidget = new class Widget { 222 this() { 223 super(s); 224 } 225 226 override void paint(WidgetPainter painter) { 227 auto c = currentColor(); 228 229 auto c1 = alphaBlend(c, Color(64, 64, 64)); 230 auto c2 = alphaBlend(c, Color(192, 192, 192)); 231 232 painter.outlineColor = c1; 233 painter.fillColor = c1; 234 painter.drawRectangle(Point(0, 0), this.width / 2, this.height / 2); 235 painter.drawRectangle(Point(this.width / 2, this.height / 2), this.width / 2, this.height / 2); 236 237 painter.outlineColor = c2; 238 painter.fillColor = c2; 239 painter.drawRectangle(Point(this.width / 2, 0), this.width / 2, this.height / 2); 240 painter.drawRectangle(Point(0, this.height / 2), this.width / 2, this.height / 2); 241 } 242 }; 243 244 auto hl = new HorizontalLayout(this); 245 auto cancelButton = new Button("Cancel", hl); 246 auto okButton = new Button("OK", hl); 247 248 recomputeChildLayout(); // FIXME hack 249 250 cancelButton.addEventListener(EventType.triggered, &Cancel); 251 okButton.addEventListener(EventType.triggered, &OK); 252 253 r.focus(); 254 } 255 256 LabeledLineEdit r; 257 LabeledLineEdit g; 258 LabeledLineEdit b; 259 LabeledLineEdit a; 260 261 LabeledLineEdit h; 262 LabeledLineEdit s; 263 LabeledLineEdit l; 264 265 LabeledLineEdit css; 266 267 Color currentColor() { 268 import std.conv; 269 try { 270 return Color(to!int(r.content), to!int(g.content), to!int(b.content), to!int(a.content)); 271 } catch(Exception e) { 272 return Color.transparent; 273 } 274 } 275 276 277 override void OK() { 278 import std.conv; 279 try { 280 onOK(Color(to!int(r.content), to!int(g.content), to!int(b.content), to!int(a.content))); 281 this.close(); 282 } catch(Exception e) { 283 auto mb = new MessageBox("Bad value"); 284 mb.show(); 285 } 286 } 287 } 288 289