1 #include "CtrlLib.h"
2 
3 namespace Upp {
4 
5 #define LLOG(x)  // RLOG(x)
6 
7 #define FIXED_COLORS
8 #define IMAGECLASS CtrlsImg // contains some default definitions
9 #define IMAGEFILE <CtrlLib/Ctrls.iml>
10 #include <Draw/iml_source.h>
11 
AdjustColor(Color c,int adj)12 Color AdjustColor(Color c, int adj)
13 {
14 	return Color(clamp(c.GetR() + adj, 0, 255),
15 	             clamp(c.GetG() + adj, 0, 255),
16 	             clamp(c.GetB() + adj, 0, 255));
17 };
18 
FaceColor(int adj)19 Color FaceColor(int adj)
20 {
21 	return AdjustColor(SColorFace(), adj);
22 };
23 
SyntheticTab(int i,int roundness,Color ink)24 void SyntheticTab(int i, int roundness, Color ink)
25 {
26 	TabCtrl::Style& s = TabCtrl::StyleDefault().Write();
27 	s.body = MakeButton(0, FaceColor(8), DPI(1), ink);
28 	Image t = MakeButton(roundness, FaceColor(decode(i, CTRL_NORMAL, -20,
29 	                                                    CTRL_HOT, 2,
30 	                                                    CTRL_PRESSED, 8,
31 	                                                    -8)), DPI(1), ink,
32 	                                                    CORNER_TOP_LEFT|CORNER_TOP_RIGHT);
33 	s.first[i] = s.last[i] = s.both[i] = s.normal[i] = ChHot(Crop(t, 0, 0, t.GetWidth(), t.GetHeight() - DPI(3)), DPI(3));
34 	s.margin = 0;
35 	s.sel = Rect(0, DPI(1), 0, DPI(1));
36 	s.extendleft = DPI(2);
37 	s.text_color[i] = SColorText();
38 }
39 
MakeDialogIcons()40 void MakeDialogIcons()
41 {
42 	auto MakeCircularIcon = [](Color c, const byte *signc, int csz, double ssz = 0.6, Color sc = White()) -> Image
43 	{
44 		String sign = ZDecompress(signc, csz);
45 		int sz = DPI(32);
46 		ImagePainter w(sz, sz);
47 		double r = 0.5 * sz;
48 		w.Clear(RGBAZero());
49 		w.Circle(r, r, r).Fill(Gray());
50 		w.Circle(r, r, r - DPI(1)).Fill(White());
51 		w.Circle(r, r, r - DPI(2)).Fill(Pointf(0.5 * r, 0.6 * r), Blend(White(), c), r, c);
52 
53 		ssz *= sz;
54 		Rectf sr = RectfC((sz - ssz) / 2, (sz - ssz) / 2, ssz, ssz);
55 		Rectf br = GetSVGPathBoundingBox(sign);
56 		double scale = sr.GetHeight() / br.GetHeight();
57 		w.Translate(sr.left + (sr.Width() - br.Width() * scale) / 2, sr.top);
58 		w.Scale(scale);
59 		w.Translate(-br.TopLeft());
60 		w.Path(sign).Stroke(2, Black()).Fill(sc);
61 
62 		return w;
63 	};
64 
65 	static const byte s_information[] = { 120,156,85,81,193,117,67,49,8,91,197,19,240,12,216,216,222,225,231,224,107,246,31,164,8,72,211,94,18,244,65,72,200,47,217,180,102,243,95,229,118,85,200,180,128,78,146,221,148,169,119,111,44,58,163,169,146,157,86,245,162,249,219,24,252,97,204,54,70,50,132,166,181,97,96,164,70,214,87,38,250,213,16,240,138,193,7,226,185,42,235,210,40,160,197,248,186,146,1,241,180,251,247,142,247,43,15,153,139,186,181,199,249,219,87,118,206,109,49,200,125,134,78,1,251,215,90,196,7,22,48,224,96,237,240,176,183,35,223,191,155,116,140,23,184,194,184,244,211,10,97,176,198,136,91,253,15,90,236,177,30,12,156,19,158,148,115,69,143,220,157,229,31,153,45,244,57,72,9,238,2,169,128,159,187,188,30,52,150,115,66,149,5,94,229,227,32,2,78,96,32,106,186,17,4,240,112,164,189,4,161,93,86,140,217,142,129,172,45,60,23,152,225,72,176,196,234,125,24,82,22,244,131,131,178,188,251,251,217,16,146,41,198,159,5,51,22,73,61,254,44,222,200,231,120,255,0,15,160,120,181 };
66 	CtrlImg::Set(CtrlImg::I_information, MakeCircularIcon(Color(85, 127, 200), s_information, sizeof(s_information), 0.6, Color(243, 255, 211)));
67 	static const byte s_question[] = { 120,156,77,84,201,145,32,33,12,75,165,35,160,240,193,21,196,60,248,110,254,129,172,36,96,119,62,93,184,13,178,44,11,126,172,149,105,223,88,197,226,219,54,74,235,223,180,82,235,103,179,244,248,102,20,235,72,172,210,215,55,91,9,251,238,26,91,235,191,196,210,9,129,172,90,124,34,49,9,181,140,95,194,174,111,249,57,209,137,187,162,100,126,38,64,172,215,64,34,9,178,146,187,204,153,62,235,109,246,63,161,114,88,86,176,157,37,38,15,163,194,16,142,19,121,247,226,141,133,177,177,169,84,101,114,55,130,204,89,86,126,103,57,74,237,239,119,242,76,39,75,54,191,136,109,235,27,131,187,81,30,13,13,81,37,73,20,29,78,116,83,215,163,114,211,246,96,173,190,120,206,215,255,245,142,70,130,39,200,74,53,239,137,204,50,218,133,74,84,50,214,0,131,221,140,204,198,100,191,77,233,233,228,183,91,146,195,236,44,123,214,232,77,77,156,96,50,0,212,156,159,213,166,76,54,158,180,170,78,210,78,63,149,140,118,104,192,102,65,102,96,57,28,65,215,169,112,154,194,76,106,69,45,19,167,220,75,176,207,41,120,111,36,127,3,236,160,123,94,164,113,33,192,80,13,72,244,15,20,1,174,69,144,39,2,114,138,20,13,108,188,17,106,249,160,250,6,54,28,2,138,144,111,168,7,207,131,174,128,114,223,90,4,60,129,143,55,9,69,73,229,206,151,228,217,23,0,105,46,12,14,238,242,94,6,246,193,156,244,171,75,60,131,35,209,10,7,216,37,13,83,40,66,134,240,1,78,81,26,202,187,216,255,6,107,10,5,121,33,101,116,161,87,185,142,242,66,87,171,242,69,160,34,247,137,238,134,13,232,75,217,7,51,129,172,11,166,117,36,68,243,220,141,179,198,205,154,246,18,83,86,74,217,3,150,160,87,82,6,24,50,92,72,73,152,86,204,58,65,224,40,176,133,236,199,93,188,53,148,12,14,78,25,245,156,208,122,187,46,216,77,152,252,223,184,149,175,2,64,70,215,85,254,245,92,252,249,129,226,236,177,155,74,186,182,223,136,83,72,5,178,134,235,229,232,149,37,92,255,172,173,131,190,52,228,118,108,176,168,184,181,206,169,237,243,4,89,211,76,94,16,154,255,139,212,12,78,17,61,23,7,186,189,234,49,202,249,26,1,235,60,151,253,210,72,249,234,250,132,55,100,138,60,31,36,92,74,224,162,47,146,57,129,204,91,95,20,186,60,118,238,21,47,10,169,229,147,89,232,131,219,105,141,118,105,4,175,151,104,232,249,226,147,64,215,52,249,234,5,33,161,94,164,103,233,158,106,122,152,30,96,83,95,183,22,53,204,127,52,142,188,151,225,83,254,144,191,67,249,61,175,63,127,1,105,41,38,184 };
68 	CtrlImg::Set(CtrlImg::I_question, MakeCircularIcon(Color(85, 127, 200), s_question, sizeof(s_question)));
69 	static const byte s_exclamation[] = { 120,156,77,80,203,17,68,33,8,107,133,10,24,249,41,246,240,246,224,117,251,47,100,1,121,51,123,81,34,36,36,126,152,113,25,248,70,85,120,120,224,24,13,14,109,180,9,206,40,4,180,144,4,22,35,109,120,72,208,29,204,179,125,104,224,86,208,89,83,183,166,20,233,134,204,164,36,131,64,4,217,162,49,113,110,144,145,12,166,60,111,125,216,106,234,54,28,151,23,195,225,8,165,199,144,10,250,173,117,228,104,55,212,210,73,48,166,180,171,39,164,66,228,250,61,44,249,182,42,213,127,220,239,39,214,199,189,29,197,107,125,8,92,16,98,22,182,71,216,211,90,19,2,52,180,204,54,184,241,27,81,201,54,139,12,23,191,122,68,158,113,239,166,6,39,190,51,162,52,34,205,144,47,139,234,71,90,176,65,239,122,145,166,195,100,217,117,232,37,24,73,219,251,95,168,239,15,254,54,92,127 };
70 	CtrlImg::Set(CtrlImg::I_exclamation, MakeCircularIcon(Red(), s_exclamation, sizeof(s_exclamation)));
71 	static const byte s_error[] = { 120,156,101,142,209,13,128,48,8,68,87,233,4,132,22,68,187,3,46,224,254,131,152,122,71,162,241,135,203,3,238,224,220,186,132,181,112,81,109,57,198,18,115,177,163,0,53,209,35,96,27,206,156,254,90,235,26,127,98,222,30,75,120,137,163,105,79,6,137,194,200,34,56,224,230,31,53,194,157,47,193,124,221,193,112,41,174 };
72 	CtrlImg::Set(CtrlImg::I_error, MakeCircularIcon(Red(), s_error, sizeof(s_error), 0.5, Yellow()));
73 }
74 
DrawClassicButton(Draw & w,const Rect & r_,Color tl1,Color br1,Color tl2,Color br2,Color face)75 void  DrawClassicButton(Draw& w, const Rect& r_, Color tl1, Color br1, Color tl2, Color br2, Color face)
76 {
77 	Rect r = r_;
78 	DrawFrame(w, r, tl1, br1);
79 	r.Deflate(1);
80 	DrawFrame(w, r, tl2, br2);
81 	r.Deflate(1);
82 	w.DrawRect(r, face);
83 }
84 
MakeClassicButton(Color tl1,Color br1,Color tl2,Color br2,Color face)85 Image MakeClassicButton(Color tl1, Color br1, Color tl2, Color br2, Color face)
86 {
87 	Rect r = Size(8, 8);
88 	ImageDraw iw(r.GetSize());
89 	DrawClassicButton(iw, r, tl1, br1, tl2, br2, face);
90 	return WithHotSpot(iw, 2, 2);
91 }
92 
ChClassicSkin()93 void ChClassicSkin()
94 {
95 	LLOG("ChInitWinClassic");
96 
97 	ChBaseSkin();
98 
99 	GUI_GlobalStyle_Write(GUISTYLE_CLASSIC);
100 	GUI_PopUpEffect_Write(Ctrl::IsCompositedGui() ? GUIEFFECT_NONE : GUIEFFECT_SLIDE);
101 
102 	Color grayface = Blend(SColorFace(), Gray());
103 
104 	Image edge = IsDarkTheme() ? MakeClassicButton(grayface, White(), Gray(), LtGray(), grayface)
105 	                           : MakeClassicButton(Gray(), White(), Black(), LtGray(), SColorFace());
106 	CtrlsImg::Set(CtrlsImg::I_EFE, edge);
107 	CtrlsImg::Set(CtrlsImg::I_VE, edge);
108 
109 	for(int i = 0; i < 6; i++)
110 		CtrlsImg::Set(CtrlsImg::I_DA + i, CtrlsImg::Get(CtrlsImg::I_kDA + i));
111 
112 	Color wg = Blend(SColorFace(), WhiteGray());
113 	{
114 		Button::Style& s = Button::StyleNormal().Write();
115 		Image edge = MakeClassicButton(wg, IsDarkTheme() ? Blend(Gray(), Black()) : Black(), White(), Gray(), SColorFace());
116 		s.look[CTRL_HOT] = s.look[CTRL_NORMAL] = IsDarkTheme() ? edge : MakeClassicButton(White(), Black(), wg, Gray(), SColorFace());
117 		s.look[CTRL_PRESSED] = MakeClassicButton(Gray(), Gray(), Gray(), Gray(), grayface);
118 		s.look[CTRL_DISABLED] = MakeClassicButton(SWhite(), Black(), wg, Gray(), SColorFace());
119 		s.monocolor[0] = s.monocolor[1] = s.monocolor[2] = s.monocolor[3] = SColorText();
120 		s.pressoffset.x = s.pressoffset.y = 1;
121 		s.transparent = false;
122 
123 		Button::Style& es = Button::StyleEdge().Write();
124 		es.look[CTRL_HOT] = es.look[CTRL_NORMAL] = edge;
125 		es.look[CTRL_PRESSED] = s.look[CTRL_PRESSED];
126 		es.look[CTRL_DISABLED] = s.look[CTRL_DISABLED];
127 
128 		for(int i = 0; i < 4; i++)
129 			Button::StyleOk().Write().look[i] = Button::StyleLeftEdge().Write().look[i] = Button::StyleScroll().Write().look[i] = es.look[i];
130 	}
131 
132 	{
133 		ScrollBar::Style& s = ScrollBar::StyleDefault().Write();
134 		auto SbWc = [&](Value *look) {
135 			Color wc = Blend(SColorFace(), Gray());
136 			look[CTRL_NORMAL] = wc;
137 			look[CTRL_HOT] = wc;
138 			look[CTRL_PRESSED] = SColorText();
139 			look[CTRL_DISABLED] = wc;
140 		};
141 
142 		SbWc(s.hupper);
143 		SbWc(s.hlower);
144 		SbWc(s.vupper);
145 		SbWc(s.vlower);
146 		for(int i = 0; i < 4; i++)
147 			s.vthumb[i] = s.hthumb[i] = Button::StyleNormal().look[i];
148 	}
149 
150 	{
151 		MultiButton::Style& s = MultiButton::StyleDefault().Write();
152 		s.border = s.trivialborder = 2;
153 	}
154 
155 	{
156 		SeparatorCtrl::Style& s = SeparatorCtrl::StyleDefault().Write();
157 		s.l1 = SColorShadow();
158 		s.l2 = SColorLight();
159 	}
160 
161 	{
162 		MenuBar::Style& s = MenuBar::StyleDefault().Write();
163 		s.popupbody = SColorFace();
164 	}
165 
166 	int c = DPI(14);
167 
168 	for(int i = 0; i < 4; i++) {
169 		{
170 			TabCtrl::Style& s = TabCtrl::StyleDefault().Write();
171 			s.body = MakeClassicButton(White(), Black(), wg, Gray(), SColorFace());
172 			Color f = i == CTRL_PRESSED ? SColorFace : grayface;
173 			Image t = MakeClassicButton(White(), Black(), wg, Gray(), f);
174 			Size isz = t.GetSize();
175 			isz.cy -= 2;
176 			ImageDraw iw(isz);
177 			iw.DrawImage(0, 0, t);
178 			if(i == CTRL_PRESSED) { // the active tab
179 				iw.DrawRect(isz.cx - 1, isz.cy - 2, 2, 1, White());
180 				iw.DrawRect(isz.cx - 2, isz.cy - 1, 2, 1, wg);
181 			}
182 			t = iw;
183 			SetHotSpots(t, Point(2, 2));
184 			s.first[i] = s.both[i] = t;
185 			ImageDraw iw2(isz);
186 			iw2.DrawImage(0, 0, t);
187 			if(i == CTRL_PRESSED) // the active tab
188 				iw2.DrawRect(0, isz.cy - 1, 1, 1, wg);
189 			t = iw2;
190 			SetHotSpots(t, Point(2, 2));
191 			s.last[i] = s.normal[i] = t;
192 			s.margin = 0;
193 			s.sel = Rect(0, 2, 0, 2);
194 			s.extendleft = 2;
195 			s.text_color[i] = SColorText();
196 		}
197 //		static int adj[] = { 10, 80, -5, -10 };
198 //		Color f = FaceColor(adj[i]);
199 		Color f = decode(i, 0, SColorPaper(), 1, Blend(SColorPaper(), SColorFace()), 2, SColorFace(), SColorFace());
200 		{
201 			for(int opt = 0; opt < 2; opt++) {
202 				ImagePainter p(c, c);
203 				p.Clear(RGBAZero());
204 				p.Circle(DPI(7), DPI(7), DPI(6)).Fill(f).Stroke(2, Pointf(DPI(2), DPI(2)), SGray(), Pointf(DPI(14), DPI(14)), SWhite());
205 				p.Circle(DPI(7), DPI(7), DPI(6) - 1).Fill(f).Stroke(1, Pointf(DPI(2) + 1, DPI(2) + 1), SBlack(), Pointf(DPI(14) - 2, DPI(14) - 2), SColorFace());
206 				if(opt)
207 					p.Circle(DPI(7), DPI(7), DPI(4)).Fill(SColorText());
208 				CtrlsImg::Set((opt ? CtrlsImg::I_S1 : CtrlsImg::I_S0) + i, p);
209 			}
210 		}
211 		{
212 			for(int chk = 0; chk < 3; chk++) {
213 				ImagePainter p(c, c);
214 				p.Clear(RGBAZero());
215 				DrawClassicButton(p, Size(c, c), SGray(), SWhite(), SBlack(), SLtGray(), f);
216 				p.Scale(DPI(1));
217 				if(chk == 1)
218 					p.Move(3, 7).Line(7, 10).Line(11, 4).Stroke(2, SColorText());
219 				if(chk == 2)
220 					p.Rectangle(3, 6, 8, 2).Fill(SColorText());
221 				CtrlsImg::Set(decode(chk, 0, CtrlsImg::I_O0, 1, CtrlsImg::I_O1, CtrlsImg::I_O2) + i, p);
222 			}
223 		}
224 	}
225 
226 	MakeDialogIcons();
227 }
228 
FillImage(Painter & p,const Rectf & r,const Image & m)229 void FillImage(Painter& p, const Rectf& r, const Image& m)
230 {
231 	Xform2D xform = Xform2D::Translation(r.left, r.top);
232 	Sizef isz = m.GetSize();
233 	xform = Xform2D::Scale(r.GetWidth() / isz.cx, r.GetHeight() / isz.cy) * xform;
234 	p.Fill(m, xform);
235 }
236 
RoundedRect(Painter & w,double x,double y,double cx,double cy,double rx,double ry,dword corners)237 void RoundedRect(Painter& w, double x, double y, double cx, double cy, double rx, double ry, dword corners)
238 {
239 	if(corners & CORNER_TOP_LEFT)
240 		w.Move(x + rx, y).Arc(x + rx, y + ry, rx, ry, -M_PI / 2, -M_PI / 2);
241 	else
242 		w.Move(x, y);
243 
244 	if(corners & CORNER_BOTTOM_LEFT)
245 		w.Line(x, y + cy - ry).Arc(x + rx, y + cy - ry, rx, ry, M_PI, -M_PI / 2);
246 	else
247 		w.Line(x, y + cy);
248 
249 	if(corners & CORNER_BOTTOM_RIGHT)
250 		w.Line(x + cx - rx, y + cy).Arc(x + cx - rx, y + cy - ry, rx, ry, M_PI / 2, -M_PI / 2);
251 	else
252 		w.Line(x + cx, y + cy);
253 
254 	if(corners & CORNER_TOP_RIGHT)
255 		w.Line(x + cx, y + ry).Arc(x + cx - rx, y + ry, rx, ry, 0, -M_PI / 2);
256 	else
257 		w.Line(x + cx, y);
258 
259 	w.Close();
260 }
261 
RoundedRect(Painter & w,Rectf r,double rx,double ry,dword corner)262 void RoundedRect(Painter& w, Rectf r, double rx, double ry, dword corner)
263 {
264 	RoundedRect(w, r.left, r.top, r.GetWidth(), r.GetHeight(), rx, ry, corner);
265 }
266 
MakeElement(Size sz,double radius,const Image & face,double border_width,Color border_color,Event<Painter &,const Rectf &> shape)267 Image MakeElement(Size sz, double radius, const Image& face, double border_width, Color border_color, Event<Painter&, const Rectf&> shape)
268 {
269 	Rectf r(0, 0, sz.cx, sz.cy);
270 	ImagePainter w(r.GetSize());
271 	w.Clear(RGBAZero());
272 	Rectf dr = r.Deflated(border_width / 2.0);
273 	if(!IsNull(face)) {
274 		shape(w, dr);
275 		w.Fill(SColorFace());
276 		FillImage(w, r.Deflated(border_width / 2.0 - 1), face);
277 	}
278 	shape(w, dr);
279 	if(!IsNull(border_color))
280 		w.Stroke(border_width, border_color);
281 	Image m = w;
282 	Point p1(int(radius + border_width), int(radius + border_width));
283 	SetHotSpots(m, p1, (Point)r.BottomRight() - p1 - Point(1, 1));
284 	return m;
285 }
286 
MakeButton(int radius,const Image & face,double border_width,Color border_color,dword corner)287 Image MakeButton(int radius, const Image& face, double border_width, Color border_color, dword corner)
288 {
289 	double q = radius + border_width + DPI(16);
290 	return MakeElement(Size((int)q, (int)q), radius, face, border_width, border_color, [&](Painter& w, const Rectf& r) {
291 		RoundedRect(w, r, radius, radius, corner);
292 	});
293 }
294 
MakeButton(int radius,Color face,double border_width,Color border_color,dword corner)295 Image MakeButton(int radius, Color face, double border_width, Color border_color, dword corner)
296 {
297 	return MakeButton(radius, CreateImage(Size(DPI(10), DPI(5)), face), border_width, border_color, corner);
298 }
299 
Hot3(const Image & m)300 Image Hot3(const Image& m)
301 {
302 	Size sz = m.GetSize();
303 	return WithHotSpots(m, sz.cx / 3, sz.cy / 3, sz.cx - sz.cx / 3, sz.cy - sz.cy / 3);
304 }
305 
ChHot(const Image & m,int n)306 Image ChHot(const Image& m, int n)
307 {
308 	return WithHotSpots(m, DPI(n), DPI(n), 0, 0);
309 }
310 
AvgColor(const Image & m,const Rect & rr)311 Color AvgColor(const Image& m, const Rect& rr)
312 {
313 	int r = 0;
314 	int g = 0;
315 	int b = 0;
316 	int n = 0;
317 	for(int y = rr.top; y < rr.bottom; y++)
318 		for(int x = rr.left; x < rr.right; x++) {
319 			RGBA c = m[y][x];
320 			Unmultiply(&c, &c, 1);
321 			if(c.a > 20) {
322 				r += c.r;
323 				g += c.g;
324 				b += c.b;
325 				n++;
326 			}
327 		}
328 	return n ? Color(r / n, g / n, b / n) : SWhite();
329 }
330 
AvgColor(const Image & m,int margin)331 Color AvgColor(const Image& m, int margin)
332 {
333 	return AvgColor(m, Rect(m.GetSize()).Deflated(margin));
334 }
335 
GetInk(const Image & m)336 Color GetInk(const Image& m)
337 {
338 	RGBA avg = AvgColor(m);
339 	Color ink = SBlack();
340 	int   best = 0;
341 	for(RGBA c : m) {
342 		Unmultiply(&c, &c, 1);
343 		if(c.a > 100) {
344 			c.a = 255;
345 			int q = Grayscale(abs(c.r - avg.r), abs(c.g - avg.g), abs(c.b - avg.b));
346 			if(q > best) {
347 				best = q;
348 				ink = c;
349 			}
350 		}
351 	}
352 	return ink;
353 }
354 
GetRoundness(const Image & m)355 int GetRoundness(const Image& m)
356 {
357 	Size isz = m.GetSize();
358 	int bestd = 0, besth = 0;
359 	int di = 0, hi = 0;
360 	int hy = isz.cy / 2;
361 	RGBA avg = AvgColor(m);
362 	auto Chk = [&](int x, int y, int& best, int& besti) {
363 		if(x < isz.cx && y < isz.cy) {
364 			RGBA c = m[y][x];
365 			Unmultiply(&c, &c, 1);
366 			if(c.a > 100) {
367 				int q = Grayscale(abs(c.r - avg.r), abs(c.g - avg.g), abs(c.b - avg.b));
368 				if(q > best) {
369 					best = q;
370 					besti = x;
371 				}
372 			}
373 		}
374 	};
375 	for(int i = 0; i < 8; i++) {
376 		Chk(i, hy, besth, hi);
377 		Chk(i, i, bestd, di);
378 	}
379 	return max(di - hi, 0);
380 }
381 
382 static Value sSample;
383 
SetChameleonSample(const Value & m,bool once)384 void SetChameleonSample(const Value& m, bool once)
385 {
386 	if(!once || IsNull(sSample))
387 		sSample = m;
388 }
389 
GetChameleonSample()390 Value GetChameleonSample()
391 {
392 	return sSample;
393 }
394 
WithRect(Image m,int x,int y,int cx,int cy,Color c)395 Image WithRect(Image m, int x, int y, int cx, int cy, Color c)
396 {
397 	ImageBuffer ib(m);
398 	for(int i = 0; i < cx; i++)
399 		for(int j = 0; j < cy; j++)
400 			ib[y + j][x + i] = c;
401 	return ib;
402 }
403 
WithLeftLine(const Image & m,Color c,int w)404 Image WithLeftLine(const Image& m, Color c, int w)
405 {
406 	return WithRect(m, 0, 0, w, m.GetHeight(), c);
407 }
408 
WithRightLine(const Image & m,Color c,int w)409 Image WithRightLine(const Image& m, Color c, int w)
410 {
411 	return WithRect(m, m.GetWidth() - w, 0, w, m.GetHeight(), c);
412 }
413 
WithTopLine(const Image & m,Color c,int w)414 Image WithTopLine(const Image& m, Color c, int w)
415 {
416 	return WithRect(m, 0, 0, m.GetWidth(), w, c);
417 }
418 
WithBottomLine(const Image & m,Color c,int w)419 Image WithBottomLine(const Image& m, Color c, int w)
420 {
421 	return WithRect(m, 0, m.GetHeight() - w, m.GetWidth(), w, c);
422 }
423 
ChSynthetic(Image * button100x100,Color * text,bool macos)424 void ChSynthetic(Image *button100x100, Color *text, bool macos)
425 {
426 	int roundness = DPI(3);
427 	int roundness2 = roundness;
428 	Color ink = SColorText();
429 	int lw = macos ? 1 : DPI(1);
430 	for(int i = 0; i < 4; i++) {
431 		Image m = button100x100[i];
432 		Image m2 = macos ? button100x100[i + 4] : m;
433 		auto Espots = [=](const Image& m) { return WithHotSpots(m, DPI(3), DPI(1), CH_EDITFIELD_IMAGE, DPI(3)); };
434 		if(i == 0) {
435 			ink = GetInk(m);
436 			if(macos && IsDarkTheme())
437 				ink = Gray();
438 			roundness = macos ? DPI(3) : GetRoundness(m) ? DPI(3) : 0;
439 			roundness2 = macos ? 0 : roundness;
440 			CtrlsImg::Set(CtrlsImg::I_EFE, Espots(MakeButton(roundness2, SColorPaper(), lw, ink)));
441 			CtrlsImg::Set(CtrlsImg::I_VE, WithHotSpots(MakeButton(DPI(0), SColorPaper(), lw, ink), DPI(2), DPI(2), 0, 0));
442 			LabelBox::SetLook(WithHotSpots(MakeButton(2 * roundness / 3, Image(), lw, ink), DPI(3), DPI(3), 0, 0));
443 		}
444 		Size sz = m.GetSize();
445 		m = Crop(m, sz.cx / 8, sz.cy / 8, 6 * sz.cx / 8, 6 * sz.cy / 8);
446 		m2 = Crop(m2, sz.cx / 8, sz.cy / 8, 6 * sz.cx / 8, 6 * sz.cy / 8);
447 		{
448 			EditField::Style& s = EditField::StyleDefault().Write();
449 			s.activeedge = true;
450 			s.edge[i] = Espots(MakeButton(roundness2,
451 			                              i == CTRL_DISABLED ? SColorFace() : SColorPaper(),
452 			                              macos && i == CTRL_PRESSED ? DPI(2) : lw,
453 			                              i == CTRL_PRESSED ? SColorHighlight() : ink));
454 			if(i == 0)
455 				s.coloredge = Espots(MakeButton(roundness2, Black(), DPI(2), Null));
456 		}
457 		{
458 			auto Set = [&](Button::Style& s, const Image& arrow = Null, Color ink2 = Null, Color border = Null) {
459 				Value l = MakeButton(0, m, DPI(1), Nvl(border, Nvl(ink2, ink)), 0);
460 				s.look[i] = IsNull(arrow) ? l : ChLookWith(l, arrow, ink2);
461 			};
462 			Color c = Blend(SColorFace(), ink);
463 			Color k = text[i];
464 
465 			Set(Button::StyleScroll().Write(), Null, k, c);
466 			Set(Button::StyleEdge().Write());
467 			Set(Button::StyleLeftEdge().Write());
468 			ScrollBar::Style& s = ScrollBar::StyleDefault().Write();
469 
470 			Set(s.up, CtrlsImg::UA(), k, c);
471 			Set(s.down, CtrlsImg::DA(), k, c);
472 			Set(s.left, CtrlsImg::LA(), k, c);
473 			Set(s.right, CtrlsImg::RA(), k, c);
474 		}
475 		{
476 			MultiButton::Style& s = MultiButton::StyleDefault().Write();
477 			s.clipedge = true;
478 			s.border = s.trivialborder = 0;
479 
480 			s.left[i] = MakeButton(roundness, m2, lw, ink, CORNER_TOP_LEFT|CORNER_BOTTOM_LEFT);
481 			s.trivial[i] = s.look[i] = s.right[i] = MakeButton(roundness, m2, lw, ink, CORNER_TOP_RIGHT|CORNER_BOTTOM_RIGHT);
482 			if(i == 0)
483 				s.coloredge = WithHotSpots(MakeButton(roundness, Black(), DPI(2), Null), DPI(3), lw, 0, 0);
484 			auto Middle = [&](Image m) {
485 				ImageBuffer ib(m);
486 				for(int y = 0; y < lw; y++)
487 					for(int x = 0; x < ib.GetWidth(); x++) {
488 						ib[y][x] = ink;
489 						ib[ib.GetHeight() - y - 1][x] = ink;
490 					}
491 				return WithHotSpot(ib, DPI(1), DPI(1));
492 			};
493 			s.lmiddle[i] = Middle(WithRightLine(m2, ink, lw));
494 			s.rmiddle[i] = Middle(WithLeftLine(m2, ink, lw));
495 			s.monocolor[i] = s.fmonocolor[i] = text[macos ? i + 4 : i];
496 			for(int i = 0; i < 4; i++)
497 				s.edge[i] = Espots(MakeButton(roundness, i == CTRL_DISABLED ? SColorFace() : SColorPaper(), lw, ink));
498 			s.margin = Rect(DPI(3), 2, lw, 2);
499 			s.activeedge = true;
500 			s.stdwidth = DPI(17);
501 		}
502 		{
503 			SpinButtons::Style& sp = SpinButtons::StyleDefault().Write();
504 			if(i == 0)
505 				sp.dec = sp.inc = Button::StyleNormal();
506 			auto Spin = [&](dword corners, const Image& sm) {
507 				return ChLookWith(WithLeftLine(MakeButton(roundness2, m, 0, Black(), corners), ink, lw), sm, text[i]);
508 			};
509 			sp.inc.look[i] = Spin(CORNER_TOP_RIGHT, CtrlImg::spinup());
510 			sp.dec.look[i] = Spin(CORNER_BOTTOM_RIGHT, CtrlImg::spindown());
511 			sp.width = DPI(16);
512 			sp.over = DPI(2);
513 		}
514 		{
515 			SpinButtons::Style& sp = SpinButtons::StyleOnSides().Write();
516 			if(i == 0)
517 				sp.dec = sp.inc = Button::StyleNormal();
518 			auto Spin = [&](dword corners, const Image& sm, bool left) {
519 				Image mm = MakeButton(roundness2, m, 0, Black(), corners);
520 				mm = left ? WithLeftLine(mm, ink, lw) : WithRightLine(mm, ink, lw);
521 				return ChLookWith(mm, sm, text[i]);
522 			};
523 			sp.inc.look[i] = Spin(CORNER_TOP_RIGHT|CORNER_BOTTOM_RIGHT, CtrlImg::plus(), true);
524 			sp.dec.look[i] = Spin(CORNER_TOP_LEFT|CORNER_BOTTOM_LEFT, CtrlImg::minus(), false);
525 			sp.width = DPI(16);
526 			sp.over = DPI(2);
527 		}
528 		{
529 			HeaderCtrl::Style& hs = HeaderCtrl::StyleDefault().Write();
530 			Image h = m;
531 			if(macos)
532 				h = CreateImage(Size(10, 10), FaceColor(decode(i, CTRL_NORMAL, 10,
533 			                                                      CTRL_HOT, IsDarkTheme() ? 15 : 0,
534 			                                                      CTRL_PRESSED, -5,
535 			                                                      -8)));
536 			hs.look[i] = ChHot(WithBottomLine(WithRightLine(h, ink, 1), ink));
537 		}
538 		if(i == CTRL_DISABLED) {
539 			ProgressIndicator::Style& s = ProgressIndicator::StyleDefault().Write();
540 			ImageBuffer ib(1, 8);
541 			ImageBuffer ibb(1, 8);
542 			Color c = macos ? AvgColor(button100x100[4]) : SColorHighlight();
543 			for(int i = 0; i < 8; i++) {
544 				int a[] = { 20, 40, 10, 0, -10, -20, -30, -40 };
545 				ib[i][0] = AdjustColor(c, a[i]);
546 				ibb[i][0] = Blend(SColorFace(), SColorPaper(), i * 255 / 7);
547 			}
548 			Image m = MakeButton(roundness, Magnify(ib, 10, 1), DPI(1), ink);
549 			s.hchunk = m;
550 			s.vchunk = RotateAntiClockwise(m);
551 			m = MakeButton(roundness, Magnify(ibb, 10, 1), DPI(1), ink);
552 			s.hlook = m;
553 			s.vlook = RotateAntiClockwise(m);
554 			s.bound = true;
555 			s.nomargins = true;
556 		}
557 		if(i == CTRL_NORMAL || i == CTRL_PRESSED) {
558 			Image sm = MakeElement(Size(DPI(10), DPI(20)), roundness,
559 			                       CreateImage(Size(10, 10), GrayColor(224 - 20 * i)),
560 			                       lw, ink, [&](Painter& w, const Rectf& r) {
561 				double cx = r.GetWidth();
562 				double cy = r.GetHeight();
563 				double uy = 0.4 * cy;
564 				double by = 0.85 * cy;
565 				double uq = 0.5 * uy;
566 				w.Move(r.left, r.top + by)
567 				 .Line(r.left, r.top + uy)
568 				 .Quadratic(r.left, r.top + uq, r.left + cx / 2, r.top)
569 				 .Quadratic(r.left + cx, r.top + uq, r.left + cx, r.top + uy)
570 				 .Line(r.left + cx, r.top + by)
571 				 .Close();
572 			});
573 			CtrlImg::Set(i == CTRL_PRESSED ? CtrlImg::I_hthumb1 : CtrlImg::I_hthumb, sm);
574 			CtrlImg::Set(i == CTRL_PRESSED ? CtrlImg::I_vthumb1 : CtrlImg::I_vthumb, RotateClockwise(sm));
575 		}
576 		{
577 			SyntheticTab(i, roundness, ink);
578 		}
579 	}
580 }
581 
ChBaseSkin()582 void ChBaseSkin()
583 {
584 	GUI_GlobalStyle_Write(GUISTYLE_XP);
585 	GUI_PopUpEffect_Write(Ctrl::IsCompositedGui() ? GUIEFFECT_NONE : GUIEFFECT_SLIDE);
586 	ColoredOverride(CtrlsImg::Iml(), CtrlsImg::Iml());
587 }
588 
ChMakeSkin(int roundness,Color button_face,Color thumb,int * adj)589 void ChMakeSkin(int roundness, Color button_face, Color thumb, int *adj)
590 {
591 	GUI_GlobalStyle_Write(GUISTYLE_XP);
592 
593 	ColoredOverride(CtrlsImg::Iml(), CtrlsImg::Iml());
594 
595 	for(int i = 0; i < 6; i++)
596 		CtrlsImg::Set(CtrlsImg::I_DA + i, CtrlsImg::Get(CtrlsImg::I_kDA + i));
597 
598 	int c = DPI(14);
599 
600 	Color text[4];
601 	Image button[4], sbutton[4];
602 
603 	Color border = Gray();
604 
605 	roundness = DPI(roundness);
606 
607 	{
608 		for(int pass = 0; pass < 2; pass++) {
609 			Button::Style& s = pass ? Button::StyleOk().Write() : Button::StyleNormal().Write();
610 			s.focusmargin = DPI(4);
611 			for(int i = 0; i < 4; i++) {
612 				Color f = AdjustColor(button_face, adj[i]);
613 				Color ink = i == CTRL_DISABLED ? SColorDisabled() : SColorText();
614 				s.look[i] = MakeButton(roundness, f, DPI(1 + pass), border);
615 				text[i] = s.monocolor[i] = s.textcolor[i] = ink;
616 				if(pass == 0) {
617 					sbutton[i] = MakeButton(roundness, f, DPI(1), border);
618 					button[i] = MakeButton(roundness ? DPI(1) : 0, f, DPI(1), border);
619 					Color f = decode(i, 0, SColorPaper(), 1, Blend(SColorPaper(), SColorFace()), 2, SColorFace(), SColorFace());
620 					{
621 						for(int opt = 0; opt < 2; opt++) {
622 							ImagePainter p(c, c);
623 							p.Scale(DPI(1));
624 							p.Clear(RGBAZero());
625 							p.Circle(7, 7, 6).Fill(f).Stroke(1, border);
626 							if(opt)
627 								p.Circle(7, 7, 4).Fill(ink);
628 							CtrlsImg::Set((opt ? CtrlsImg::I_S1 : CtrlsImg::I_S0) + i, p);
629 						}
630 					}
631 					{
632 						for(int chk = 0; chk < 3; chk++) {
633 							ImagePainter p(c, c);
634 							p.Scale(DPI(1));
635 							p.Clear(RGBAZero());
636 							p.Rectangle(1, 1, 12, 12).Fill(f).Stroke(1, border);
637 							if(chk == 1)
638 								p.Move(3, 7).Line(7, 10).Line(11, 4).Stroke(2, ink);
639 							if(chk == 2)
640 								p.Rectangle(3, 6, 8, 2).Fill(ink);
641 							CtrlsImg::Set(decode(chk, 0, CtrlsImg::I_O0, 1, CtrlsImg::I_O1, CtrlsImg::I_O2) + i, p);
642 						}
643 					}
644 				}
645 			}
646 		}
647 
648 		ChSynthetic(sbutton, text);
649 
650 		{
651 			auto& s = ToolButton::StyleDefault().Write();
652 			s.look[CTRL_NORMAL] = Image();
653 			s.look[CTRL_HOT] = button[CTRL_HOT];
654 			s.look[CTRL_PRESSED] = button[CTRL_PRESSED];
655 			s.look[CTRL_DISABLED] = Image();
656 			s.look[CTRL_CHECKED] = button[CTRL_PRESSED];
657 			s.look[CTRL_HOTCHECKED] = button[CTRL_HOT];
658 		}
659 
660 		CtrlImg::Set(CtrlImg::I_MenuCheck0, CtrlsImg::O0());
661 		CtrlImg::Set(CtrlImg::I_MenuCheck1, CtrlsImg::O1());
662 		CtrlImg::Set(CtrlImg::I_MenuRadio0, CtrlsImg::S0());
663 		CtrlImg::Set(CtrlImg::I_MenuRadio1, CtrlsImg::S1());
664 	}
665 
666 	{
667 		ScrollBar::Style& s = ScrollBar::StyleDefault().Write();
668 		ImagePainter p(c, c);
669 		p.Rectangle(0, 0, c, c).Fill(0, 0, IsDarkTheme() ? SColorFace() : AdjustColor(thumb, 40), c, 0, IsDarkTheme() ? LtGray() : SColorPaper());
670 		Image vtrough = p;
671 
672 		for(int status = CTRL_NORMAL; status <= CTRL_DISABLED; status++) {
673 			s.hupper[status] = s.hlower[status] = ChHot(RotateClockwise(vtrough));
674 			s.vupper[status] = s.vlower[status] = ChHot(vtrough);
675 			static int adj[] = { 0, 10, -10, -20 };
676 			s.hthumb[status] = s.vthumb[status] = AdjustColor(thumb, adj[status]);
677 		}
678 	}
679 
680 	{
681 		MenuBar::Style& s = MenuBar::StyleDefault().Write();
682 		s.topitem[1] = Blend(SColorHighlight(), SColorPaper());
683 		s.icheck = button[CTRL_PRESSED];
684 	}
685 	GUI_PopUpEffect_Write(Ctrl::IsCompositedGui() ? GUIEFFECT_NONE : GUIEFFECT_SLIDE);
686 
687 	MakeDialogIcons();
688 }
689 
ChStdSkin()690 void ChStdSkin()
691 {
692 	ChReset();
693 	static int adj[] = { 10, 80, -5, -10 };
694 	SColorFace_Write(Color(240, 240, 240));
695 	SColorMenu_Write(Color(240, 240, 240));
696 	SColorHighlight_Write(Color(50, 50, 250));
697 	ChMakeSkin(3, SColorFace(), SLtGray(), adj);
698 }
699 
ChGraySkin()700 void ChGraySkin()
701 {
702 	ChReset();
703 	static int adj[] = { 0, 70, -15, -20 };
704 	SColorHighlight_Write(Gray());
705 	ChMakeSkin(3, SWhiteGray(), SLtGray(), adj);
706 }
707 
ChDarkSkin()708 void ChDarkSkin()
709 {
710 	ChReset();
711 	static int adj[] = { 10, 80, -5, -10 };
712 	SColorPaper_Write(Black());
713 	SColorHighlight_Write(Gray());
714 	SColorHighlightText_Write(White());
715 	ChMakeSkin(3, SWhiteGray(), SWhiteGray(), adj);
716 }
717 
ChFlatSkin()718 void ChFlatSkin()
719 {
720 	ChReset();
721 	static int adj[] = { 10, 80, -5, -10 };
722 	SColorFace_Write(Color(240, 240, 240));
723 	SColorMenu_Write(Color(240, 240, 240));
724 	SColorHighlight_Write(Color(50, 50, 250));
725 	ChMakeSkin(0, SColorFace(), SLtGray(), adj);
726 }
727 
ChFlatGraySkin()728 void ChFlatGraySkin()
729 {
730 	ChReset();
731 	static int adj[] = { 0, 70, -15, -20 };
732 	SColorHighlight_Write(Gray());
733 	ChMakeSkin(0, SWhiteGray(), SLtGray(), adj);
734 }
735 
ChFlatDarkSkin()736 void ChFlatDarkSkin()
737 {
738 	ChReset();
739 	static int adj[] = { 10, 80, -5, -10 };
740 	SColorPaper_Write(Black());
741 	SColorHighlight_Write(Gray());
742 	SColorHighlightText_Write(White());
743 	ChMakeSkin(0, SWhiteGray(), SWhiteGray(), adj);
744 }
745 
746 #ifdef GUI_X11
747 
ChHostSkin()748 void ChHostSkin()
749 {
750 	int h = Ctrl::GetPrimaryScreenArea().Height();
751 	Font::SetDefaultFont(Arial(h > 1300 ? 26 : h > 800 ? 14 : 12));
752 	SColorFace_Write(Color(242, 241, 240));
753 	SColorMenu_Write(Color(242, 241, 240));
754 	SColorHighlight_Write(Color(50, 50, 250));
755 
756 	ChStdSkin();
757 }
758 
759 #endif
760 
GetAllChSkins()761 Vector<Tuple<void (*)(), String>> GetAllChSkins()
762 {
763 	return Vector<Tuple<void (*)(), String>> {
764 		{ ChHostSkin, "Host platform" },
765 	    { ChClassicSkin, "Classic" },
766 		{ ChStdSkin, "Standard" },
767 		{ ChGraySkin, "Gray" },
768 		{ ChDarkSkin, "Dark" },
769 		{ ChFlatSkin, "Flat" },
770 		{ ChFlatGraySkin, "Flat Gray" },
771 		{ ChFlatDarkSkin, "Flat Dark" }
772 	};
773 }
774 
775 }
776