#include "CtrlLib.h" namespace Upp { #define LLOG(x) // RLOG(x) #define FIXED_COLORS #define IMAGECLASS CtrlsImg #define IMAGEFILE #include // #define FIXED_COLORS // #define FIXED_SIZE #define IMAGECLASS ClassicCtrlsImg #define IMAGEFILE #include void ChSysInit(); void SbWc(Value *look) { Color wc = Blend(SColorFace(), SColorPaper(), 170); look[CTRL_NORMAL] = wc; look[CTRL_HOT] = wc; look[CTRL_PRESSED] = SColorText(); look[CTRL_DISABLED] = wc; } void ChClassicSkin() { LLOG("ChInitWinClassic"); ChSysInit(); GUI_GlobalStyle_Write(GUISTYLE_CLASSIC); GUI_PopUpEffect_Write(Ctrl::IsCompositedGui() ? GUIEFFECT_NONE : GUIEFFECT_SLIDE); ColoredOverride(CtrlsImg::Iml(), ClassicCtrlsImg::Iml()); for(int q = 0; q < 4; q++) CtrlsImg::Set(CtrlsImg::I_HTB + q, AdjustColors(CtrlsImg::Get(ClassicCtrlsImg::I_B + q))); { Button::Style& s = Button::StyleNormal().Write(); s.monocolor[0] = s.monocolor[1] = s.monocolor[2] = s.monocolor[3] = SColorText(); s.pressoffset.x = s.pressoffset.y = 1; s.transparent = false; } { ScrollBar::Style& s = ScrollBar::StyleDefault().Write(); SbWc(s.hupper); SbWc(s.hlower); SbWc(s.vupper); SbWc(s.vlower); for(int i = 0; i < 4; i++) s.vthumb[i] = s.hthumb[i] = Button::StyleNormal().look[i]; } { MultiButton::Style& s = MultiButton::StyleDefault().Write(); s.border = s.trivialborder = 2; } { SeparatorCtrl::Style& s = SeparatorCtrl::StyleDefault().Write(); s.l1 = SColorShadow(); s.l2 = SColorLight(); } { MenuBar::Style& s = MenuBar::StyleDefault().Write(); s.popupbody = SColorFace(); } // LabelBoxTextColor_Write(SColorText()); } #ifdef PLATFORM_X11 void ChSysInit() { CtrlImg::Reset(); CtrlsImg::Reset(); ChReset(); } #endif void FillImage(Painter& p, const Rectf& r, const Image& m) { Xform2D xform = Xform2D::Translation(r.left, r.top); Sizef isz = m.GetSize(); xform = Xform2D::Scale(r.GetWidth() / isz.cx, r.GetHeight() / isz.cy) * xform; p.Fill(m, xform); } void RoundedRect(Painter& w, double x, double y, double cx, double cy, double rx, double ry, dword corners) { if(corners & CORNER_TOP_LEFT) w.Move(x + rx, y).Arc(x + rx, y + ry, rx, ry, -M_PI / 2, -M_PI / 2); else w.Move(x, y); if(corners & CORNER_BOTTOM_LEFT) w.Line(x, y + cy - ry).Arc(x + rx, y + cy - ry, rx, ry, M_PI, -M_PI / 2); else w.Line(x, y + cy); if(corners & CORNER_BOTTOM_RIGHT) w.Line(x + cx - rx, y + cy).Arc(x + cx - rx, y + cy - ry, rx, ry, M_PI / 2, -M_PI / 2); else w.Line(x + cx, y + cy); if(corners & CORNER_TOP_RIGHT) w.Line(x + cx, y + ry).Arc(x + cx - rx, y + ry, rx, ry, 0, -M_PI / 2); else w.Line(x + cx, y); w.Close(); } void RoundedRect(Painter& w, Rectf r, double rx, double ry, dword corner) { RoundedRect(w, r.left, r.top, r.GetWidth(), r.GetHeight(), rx, ry, corner); } Image MakeElement(Size sz, double radius, const Image& face, double border_width, Color border_color, Event shape) { Rectf r(0, 0, sz.cx, sz.cy); ImagePainter w(r.GetSize()); w.Clear(RGBAZero()); Rectf dr = r.Deflated(border_width / 2.0); if(!IsNull(face)) { shape(w, dr); w.Fill(SColorFace()); FillImage(w, r.Deflated(border_width / 2.0 - 1), face); } shape(w, dr); if(!IsNull(border_color)) w.Stroke(border_width, border_color); Image m = w; Point p1(int(radius + border_width), int(radius + border_width)); SetHotSpots(m, p1, (Point)r.BottomRight() - p1 - Point(1, 1)); return m; } Image MakeButton(int radius, const Image& face, double border_width, Color border_color, dword corner) { double q = radius + border_width + DPI(16); return MakeElement(Size((int)q, (int)q), radius, face, border_width, border_color, [&](Painter& w, const Rectf& r) { RoundedRect(w, r, radius, radius, corner); }); } Image MakeButton(int radius, Color face, double border_width, Color border_color, dword corner) { return MakeButton(radius, CreateImage(Size(DPI(10), DPI(5)), face), border_width, border_color, corner); } Image Hot3(const Image& m) { Size sz = m.GetSize(); return WithHotSpots(m, sz.cx / 3, sz.cy / 3, sz.cx - sz.cx / 3, sz.cy - sz.cy / 3); } Image ChHot(const Image& m, int n) { return WithHotSpots(m, DPI(n), DPI(n), 0, 0); } Color AvgColor(const Image& m, const Rect& rr) { int r = 0; int g = 0; int b = 0; int n = 0; for(int y = rr.top; y < rr.bottom; y++) for(int x = rr.left; x < rr.right; x++) { RGBA c = m[y][x]; Unmultiply(&c, &c, 1); if(c.a > 20) { r += c.r; g += c.g; b += c.b; n++; } } return n ? Color(r / n, g / n, b / n) : SWhite(); } Color AvgColor(const Image& m, int margin) { return AvgColor(m, Rect(m.GetSize()).Deflated(margin)); } Color GetInk(const Image& m) { RGBA avg = AvgColor(m); Color ink = SBlack(); int best = 0; for(RGBA c : m) { Unmultiply(&c, &c, 1); if(c.a > 100) { c.a = 255; int q = Grayscale(abs(c.r - avg.r), abs(c.g - avg.g), abs(c.b - avg.b)); if(q > best) { best = q; ink = c; } } } return ink; } int GetRoundness(const Image& m) { Size isz = m.GetSize(); int bestd = 0, besth = 0; int di = 0, hi = 0; int hy = isz.cy / 2; RGBA avg = AvgColor(m); auto Chk = [&](int x, int y, int& best, int& besti) { if(x < isz.cx && y < isz.cy) { RGBA c = m[y][x]; Unmultiply(&c, &c, 1); if(c.a > 100) { int q = Grayscale(abs(c.r - avg.r), abs(c.g - avg.g), abs(c.b - avg.b)); if(q > best) { best = q; besti = x; } } } }; for(int i = 0; i < 8; i++) { Chk(i, hy, besth, hi); Chk(i, i, bestd, di); } return max(di - hi, 0); } static Value sSample; void SetChameleonSample(const Value& m, bool once) { if(!once || IsNull(sSample)) sSample = m; } Value GetChameleonSample() { return sSample; } Image WithRect(Image m, int x, int y, int cx, int cy, Color c) { ImageBuffer ib(m); for(int i = 0; i < cx; i++) for(int j = 0; j < cy; j++) ib[y + j][x + i] = c; return ib; } Image WithLeftLine(const Image& m, Color c, int w) { return WithRect(m, 0, 0, w, m.GetHeight(), c); } Image WithRightLine(const Image& m, Color c, int w) { return WithRect(m, m.GetWidth() - w, 0, w, m.GetHeight(), c); } Image WithTopLine(const Image& m, Color c, int w) { return WithRect(m, 0, 0, m.GetWidth(), w, c); } Image WithBottomLine(const Image& m, Color c, int w) { return WithRect(m, 0, m.GetHeight() - w, m.GetWidth(), w, c); } Color AdjustColor(Color c, int adj) { return Color(clamp(c.GetR() + adj, 0, 255), clamp(c.GetG() + adj, 0, 255), clamp(c.GetB() + adj, 0, 255)); } void ChSynthetic(Image *button100x100, Color *text, bool macos) { int roundness = DPI(3); int roundness2 = roundness; Color ink = SColorText(); int lw = macos ? 1 : DPI(1); for(int i = 0; i < 4; i++) { Image m = button100x100[i]; Image m2 = macos ? button100x100[i + 4] : m; auto Espots = [=](const Image& m) { return WithHotSpots(m, DPI(3), DPI(1), CH_EDITFIELD_IMAGE, DPI(3)); }; auto Face = [](int adj) { return AdjustColor(SColorFace(), adj); }; if(i == 0) { ink = GetInk(m); if(macos && IsDarkTheme()) ink = Gray(); roundness = macos ? DPI(3) : GetRoundness(m) ? DPI(3) : 0; roundness2 = macos ? 0 : roundness; CtrlsImg::Set(CtrlsImg::I_EFE, Espots(MakeButton(roundness2, SColorPaper(), lw, ink))); CtrlsImg::Set(CtrlsImg::I_VE, WithHotSpots(MakeButton(DPI(0), SColorPaper(), lw, ink), DPI(2), DPI(2), 0, 0)); LabelBox::SetLook(WithHotSpots(MakeButton(2 * roundness / 3, Image(), lw, ink), DPI(3), DPI(3), 0, 0)); } Size sz = m.GetSize(); m = Crop(m, sz.cx / 8, sz.cy / 8, 6 * sz.cx / 8, 6 * sz.cy / 8); m2 = Crop(m2, sz.cx / 8, sz.cy / 8, 6 * sz.cx / 8, 6 * sz.cy / 8); { EditField::Style& s = EditField::StyleDefault().Write(); s.activeedge = true; s.edge[i] = Espots(MakeButton(roundness2, i == CTRL_DISABLED ? SColorFace() : SColorPaper(), lw, ink)); if(i == 0) s.coloredge = Espots(MakeButton(roundness2, Black(), DPI(2), Null)); } { auto Set = [&](Button::Style& s, const Image& arrow = Null, Color ink2 = Null, Color border = Null) { Value l = MakeButton(0, m, DPI(1), Nvl(border, Nvl(ink2, ink)), 0); s.look[i] = IsNull(arrow) ? l : ChLookWith(l, arrow, ink2); }; Color c = Blend(SColorFace(), ink); Color k = text[i]; Set(Button::StyleScroll().Write(), Null, k, c); Set(Button::StyleEdge().Write()); Set(Button::StyleLeftEdge().Write()); ScrollBar::Style& s = ScrollBar::StyleDefault().Write(); Set(s.up, CtrlsImg::UA(), k, c); Set(s.down, CtrlsImg::DA(), k, c); Set(s.left, CtrlsImg::LA(), k, c); Set(s.right, CtrlsImg::RA(), k, c); } { MultiButton::Style& s = MultiButton::StyleDefault().Write(); s.clipedge = true; s.border = s.trivialborder = 0; s.left[i] = MakeButton(roundness, m2, lw, ink, CORNER_TOP_LEFT|CORNER_BOTTOM_LEFT); s.trivial[i] = s.look[i] = s.right[i] = MakeButton(roundness, m2, lw, ink, CORNER_TOP_RIGHT|CORNER_BOTTOM_RIGHT); if(i == 0) s.coloredge = WithHotSpots(MakeButton(roundness, Black(), macos ? lw : DPI(2), Null), DPI(3), lw, 0, 0); auto Middle = [&](Image m) { ImageBuffer ib(m); for(int y = 0; y < lw; y++) for(int x = 0; x < ib.GetWidth(); x++) { ib[y][x] = ink; ib[ib.GetHeight() - y - 1][x] = ink; } return WithHotSpot(ib, DPI(1), DPI(1)); }; s.lmiddle[i] = Middle(WithRightLine(m2, ink, lw)); s.rmiddle[i] = Middle(WithLeftLine(m2, ink, lw)); s.monocolor[i] = s.fmonocolor[i] = text[macos ? i + 4 : i]; for(int i = 0; i < 4; i++) s.edge[i] = Espots(MakeButton(roundness, i == CTRL_DISABLED ? SColorFace() : SColorPaper(), lw, ink)); s.margin = Rect(DPI(3), 2, lw, 2); s.activeedge = true; s.stdwidth = DPI(17); } { SpinButtons::Style& sp = SpinButtons::StyleDefault().Write(); if(i == 0) sp.dec = sp.inc = Button::StyleNormal(); auto Spin = [&](dword corners, const Image& sm) { return ChLookWith(WithLeftLine(MakeButton(roundness2, m, 0, Black(), corners), ink, lw), sm, text[i]); }; sp.inc.look[i] = Spin(CORNER_TOP_RIGHT, CtrlImg::spinup()); sp.dec.look[i] = Spin(CORNER_BOTTOM_RIGHT, CtrlImg::spindown()); sp.width = DPI(16); sp.over = DPI(2); } { SpinButtons::Style& sp = SpinButtons::StyleOnSides().Write(); if(i == 0) sp.dec = sp.inc = Button::StyleNormal(); auto Spin = [&](dword corners, const Image& sm, bool left) { Image mm = MakeButton(roundness2, m, 0, Black(), corners); mm = left ? WithLeftLine(mm, ink, lw) : WithRightLine(mm, ink, lw); return ChLookWith(mm, sm, text[i]); }; sp.inc.look[i] = Spin(CORNER_TOP_RIGHT|CORNER_BOTTOM_RIGHT, CtrlImg::plus(), true); sp.dec.look[i] = Spin(CORNER_TOP_LEFT|CORNER_BOTTOM_LEFT, CtrlImg::minus(), false); sp.width = DPI(16); sp.over = DPI(2); } { HeaderCtrl::Style& hs = HeaderCtrl::StyleDefault().Write(); Image h = m; if(macos) h = CreateImage(Size(10, 10), Face(decode(i, CTRL_NORMAL, 10, CTRL_HOT, IsDarkTheme() ? 15 : 0, CTRL_PRESSED, -5, -8))); hs.look[i] = ChHot(WithBottomLine(WithRightLine(h, ink, 1), ink)); } if(i == CTRL_DISABLED) { ProgressIndicator::Style& s = ProgressIndicator::StyleDefault().Write(); ImageBuffer ib(1, 8); ImageBuffer ibb(1, 8); Color c = macos ? AvgColor(button100x100[4]) : SColorHighlight(); for(int i = 0; i < 8; i++) { int a[] = { 20, 40, 10, 0, -10, -20, -30, -40 }; ib[i][0] = AdjustColor(c, a[i]); ibb[i][0] = Blend(SColorFace(), SColorPaper(), i * 255 / 7); } s.hchunk = MakeButton(roundness, Magnify(ib, 10, 1), DPI(1), ink); s.hlook = MakeButton(roundness, Magnify(ibb, 10, 1), DPI(1), ink); s.bound = true; s.nomargins = true; } if(i == CTRL_NORMAL || i == CTRL_PRESSED) { Image sm = MakeElement(Size(DPI(10), DPI(20)), roundness, m, lw, ink, [&](Painter& w, const Rectf& r) { double cx = r.GetWidth(); double cy = r.GetHeight(); double uy = 0.4 * cy; double uq = 0.5 * uy; w.Move(r.left, r.top + cy) .Line(r.left, r.top + uy) .Quadratic(r.left, r.top + uq, r.left + cx / 2, r.top) .Quadratic(r.left + cx, r.top + uq, r.left + cx, r.top + uy) .Line(r.left + cx, r.top + cy) .Close(); }); CtrlImg::Set(i == CTRL_PRESSED ? CtrlImg::I_hthumb1 : CtrlImg::I_hthumb, sm); CtrlImg::Set(i == CTRL_PRESSED ? CtrlImg::I_vthumb1 : CtrlImg::I_vthumb, RotateClockwise(sm)); } { TabCtrl::Style& s = TabCtrl::StyleDefault().Write(); s.body = MakeButton(0, Face(8), DPI(1), ink); Image t = MakeButton(roundness, Face(decode(i, CTRL_NORMAL, -20, CTRL_HOT, 2, CTRL_PRESSED, 8, -8)), DPI(1), ink, CORNER_TOP_LEFT|CORNER_TOP_RIGHT); 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)); s.margin = 0; s.sel = Rect(0, DPI(1), 0, DPI(1)); s.extendleft = DPI(2); s.text_color[i] = SColorText(); } } } void ChBaseSkin() { ChSysInit(); GUI_GlobalStyle_Write(GUISTYLE_XP); GUI_PopUpEffect_Write(Ctrl::IsCompositedGui() ? GUIEFFECT_NONE : GUIEFFECT_SLIDE); ColoredOverride(CtrlsImg::Iml(), CtrlsImg::Iml()); } void ChStdSkin() { ColoredOverride(CtrlsImg::Iml(), CtrlsImg::Iml()); for(int i = 0; i < 6; i++) CtrlsImg::Set(CtrlsImg::I_DA + i, CtrlsImg::Get(CtrlsImg::I_kDA + i)); int c = DPI(14); Color text[4]; Image button[4], sbutton[4]; auto Adjust = [](Color c, int adj) { return Color(clamp(c.GetR() + adj, 0, 255), clamp(c.GetG() + adj, 0, 255), clamp(c.GetB() + adj, 0, 255)); }; Color border = Gray(); { for(int pass = 0; pass < 2; pass++) { Button::Style& s = pass ? Button::StyleOk().Write() : Button::StyleNormal().Write(); int roundness = DPI(3); s.focusmargin = DPI(4); for(int i = 0; i < 4; i++) { static int adj[] = { 10, 80, -5, -10 }; Color f = Adjust(SColorFace(), adj[i]); Color ink = i == CTRL_DISABLED ? SColorDisabled() : SColorText(); s.look[i] = MakeButton(roundness, f, DPI(1 + pass), border); text[i] = s.monocolor[i] = s.textcolor[i] = ink; if(pass == 0) { sbutton[i] = MakeButton(DPI(3), f, DPI(1), border); button[i] = MakeButton(DPI(1), f, DPI(1), border); { for(int opt = 0; opt < 2; opt++) { ImagePainter p(c, c); p.Scale(DPI(1)); p.Clear(RGBAZero()); p.Circle(7, 7, 6).Fill(f).Stroke(1, border); if(opt) p.Circle(7, 7, 4).Fill(ink); CtrlsImg::Set((opt ? CtrlsImg::I_S1 : CtrlsImg::I_S0) + i, p); } } { for(int chk = 0; chk < 3; chk++) { ImagePainter p(c, c); p.Scale(DPI(1)); p.Clear(RGBAZero()); p.Rectangle(1, 1, 12, 12).Fill(f).Stroke(1, border); if(chk == 1) p.Move(3, 7).Line(7, 10).Line(11, 4).Stroke(2, ink); if(chk == 2) p.Rectangle(3, 6, 8, 2).Fill(ink); CtrlsImg::Set(decode(chk, 0, CtrlsImg::I_O0, 1, CtrlsImg::I_O1, CtrlsImg::I_O2) + i, p); } } } } } ChSynthetic(sbutton, text); { auto& s = ToolButton::StyleDefault().Write(); s.look[CTRL_NORMAL] = Image(); s.look[CTRL_HOT] = button[CTRL_HOT]; s.look[CTRL_PRESSED] = button[CTRL_PRESSED]; s.look[CTRL_DISABLED] = Image(); s.look[CTRL_CHECKED] = button[CTRL_PRESSED]; s.look[CTRL_HOTCHECKED] = button[CTRL_HOT]; } CtrlImg::Set(CtrlImg::I_MenuCheck0, CtrlsImg::O0()); CtrlImg::Set(CtrlImg::I_MenuCheck1, CtrlsImg::O1()); CtrlImg::Set(CtrlImg::I_MenuRadio0, CtrlsImg::S0()); CtrlImg::Set(CtrlImg::I_MenuRadio1, CtrlsImg::S1()); } { ScrollBar::Style& s = ScrollBar::StyleDefault().Write(); ImagePainter p(c, c); p.Rectangle(0, 0, c, c).Fill(0, 0, SColorFace(), c, 0, SColorPaper()); Image vtrough = p; for(int status = CTRL_NORMAL; status <= CTRL_DISABLED; status++) { s.hupper[status] = s.hlower[status] = ChHot(RotateClockwise(vtrough)); s.vupper[status] = s.vlower[status] = ChHot(vtrough); // we have problems getting this right for vertical static int adj[] = { 20, 40, 10, -10 }; s.hthumb[status] = s.vthumb[status] = Adjust(border, adj[status]); } } { MenuBar::Style& s = MenuBar::StyleDefault().Write(); s.topitem[1] = Blend(SColorHighlight(), SColorPaper()); } GUI_PopUpEffect_Write(Ctrl::IsCompositedGui() ? GUIEFFECT_NONE : GUIEFFECT_SLIDE); } #ifdef GUI_X11 void ChHostSkin() { int h = Ctrl::GetPrimaryScreenArea().Height(); Font::SetDefaultFont(Arial(h > 1300 ? 26 : h > 800 ? 14 : 12)); SColorFace_Write(Color(242, 241, 240)); SColorMenu_Write(Color(242, 241, 240)); SColorHighlight_Write(Color(50, 50, 250)); ChStdSkin(); } #endif }