/* * Elements of GUI Gadgets * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/element.cpp */ #include #include #include #include #include #if defined(STD_THREAD_NOT_SUPPORTED) #include #else #include #endif namespace nana { //Element definitions namespace element { namespace detail { void factory_abstract::destroy(element_abstract* ptr) { delete ptr; } } class crook : public crook_interface { bool draw(graph_reference graph, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle& r, element_state es, const data& crook_data) override { if(crook_data.radio) { unsigned bmp_unchecked[12][12] = { {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xCFD0D0, 0xAAABAB, 0x919292, 0x919292, 0xAAABAB, 0xCFD0D0, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0xA3A4A4, 0xB9BABA, 0xDBDBDB, 0xF2F2F2, 0xF2F2F2, 0xDBDBDB, 0xB9BABA, 0xA3A4A4, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xA2A3A3, 0xC3C3C3, 0xEDEDEE, 0xC6C9CD, 0xB5BABF, 0xB5BABF, 0xC8CBCE, 0xEDEEEE, 0xC3C3C3, 0xA2A3A3, 0xFFFFFF}, {0xCFD0D0, 0xB9BABA, 0xE9EAEB, 0xB3B8BD, 0xBDC2C7, 0xC8CDD2, 0xC9CED3, 0xC5C8CC, 0xBEC1C5, 0xEBECEC, 0xB9BABA, 0xCFD0D0}, {0xA9A9A9, 0xDCDCDC, 0xC5C8CC, 0xBEC3C9, 0xCBCFD5, 0xCED2D7, 0xD5D8DC, 0xDCDEE0, 0xD3D4D7, 0xD4D5D5, 0xDCDCDC, 0xA9A9A9}, {0x919292, 0xF2F2F2, 0xB4B9BD, 0xCDD1D6, 0xD3D6DA, 0xDBDDDF, 0xE4E4E5, 0xE9E9E9, 0xE8E8E9, 0xD0D1D2, 0xF2F2F2, 0x919292}, {0x919292, 0xF2F2F2, 0xBBBEC2, 0xD7DADD, 0xE0E1E3, 0xE9E9E9, 0xEFEFEF, 0xF0F0F0, 0xEFEFF0, 0xDBDCDC, 0xEFEFEF, 0x939494}, {0xA7A8A8, 0xDDDDDD, 0xCFD1D3, 0xD5D6D8, 0xE9E9E9, 0xEFEFEF, 0xF4F4F4, 0xF5F5F5, 0xEEEEEE, 0xE8E8E8, 0xDDDDDD, 0xA7A8A8}, {0xCECECE, 0xBABBBB, 0xECECED, 0xCDCECF, 0xE1E2E2, 0xF0F0F0, 0xF4F4F4, 0xF1F1F1, 0xEBEBEB, 0xF2F2F2, 0xBABBBB, 0xCECECE}, {0xFFFFFF, 0xA2A3A3, 0xC3C3C3, 0xF0F0F1, 0xE2E3E3, 0xE4E4E5, 0xE9EAEA, 0xEEEEEF, 0xF3F3F3, 0xC3C3C3, 0xA2A3A3, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0xA2A3A3, 0xBABBBB, 0xDBDBDB, 0xF4F4F4, 0xF4F4F4, 0xDCDCDC, 0xBABBBB, 0xA2A3A3, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xCECECE, 0xAAABAB, 0x8E8F8F, 0x8E8F8F, 0xA9A9A9, 0xCECECE, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF} }; unsigned bmp_unchecked_highlight[12][12] = { {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xB7CCD8, 0x7FA4BA, 0x5989A5, 0x5989A5, 0x7FA4BA, 0xB7CCD8, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0x759DB4, 0x8FB7C8, 0xBCDDE5, 0xDBF6F8, 0xDBF6F8, 0xBCDDE5, 0x8FB7C8, 0x759DB4, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0x739BB3, 0x9CC2D0, 0xD3F4FA, 0x9BD7F9, 0x84CBF9, 0x84CBF9, 0x9CD8F9, 0xD4F4FA, 0x9CC2D0, 0x739BB3, 0xFFFFFF}, {0xB7CCD8, 0x8FB7C8, 0xCFF1FA, 0x80CAF9, 0x96D3FB, 0xAADDFD, 0xABDDFD, 0x9AD5FB, 0x86CEF9, 0xCFF2FA, 0x8FB7C8, 0xB7CCD8}, {0x7DA2B9, 0xBEDEE6, 0x9AD7F9, 0x98D5FB, 0xB1DFFD, 0xB2E0FD, 0xB7E3FD, 0xBCE5FD, 0xA6DCFB, 0xA1DCF9, 0xBEDEE6, 0x7DA2B9}, {0x5989A5, 0xDBF6F8, 0x80CAF9, 0xAFDEFD, 0xB6E2FD, 0xBBE5FD, 0xC1E8FD, 0xC5EAFD, 0xC7EBFD, 0x99D8FA, 0xDBF6F8, 0x5989A5}, {0x5989A5, 0xDBF6F8, 0x84CDF9, 0xB6E2FD, 0xBFE7FD, 0xC7EBFD, 0xD5F0FE, 0xDAF2FE, 0xD8F1FE, 0xB1E1FB, 0xD8F4F6, 0x5D8CA7}, {0x7BA1B8, 0xBFDFE7, 0x9FDBF9, 0xA7DDFB, 0xC8EBFD, 0xD6F1FE, 0xE2F5FE, 0xE5F6FE, 0xD8F0FD, 0xCAEDFB, 0xBFDFE7, 0x7BA1B8}, {0xB5CAD7, 0x91B8C9, 0xCFF2FA, 0x92D5F9, 0xBAE5FC, 0xDAF2FE, 0xE4F5FE, 0xDFF3FE, 0xD2EEFD, 0xDBF7FA, 0x91B8C9, 0xB5CAD7}, {0xFFFFFF, 0x739BB3, 0x9CC2D0, 0xD7F6FA, 0xBDE8FB, 0xC2E8FC, 0xD0EDFD, 0xD7F2FC, 0xDDF8FA, 0x9CC2D0, 0x739BB3, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0x739BB3, 0x91B8C9, 0xBCDDE5, 0xDEF9FA, 0xDEF9FA, 0xBEDEE6, 0x91B8C9, 0x739BB3, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xB5CAD7, 0x7FA4BA, 0x5586A3, 0x5586A3, 0x7DA2B9, 0xB5CAD7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF} }; unsigned bmp_checked[12][12] = { {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xCFD0D0, 0xAAABAB, 0x919292, 0x919292, 0xAAABAB, 0xCFD0D0, 0xFCFCFC, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0xA3A4A4, 0xB9BABA, 0xDBDBDB, 0xF2F2F2, 0xF2F2F2, 0xDBDBDB, 0xB9BABA, 0xA3A4A4, 0xF3F3F3, 0xFFFFFF}, {0xFFFFFF, 0xA2A3A3, 0xC3C3C3, 0xEDEDEE, 0xBABFC5, 0x85939F, 0x85939F, 0xBCC1C5, 0xEDEEEE, 0xC3C3C3, 0xA2A3A3, 0xFCFCFC}, {0xCFD0D0, 0xB9BABA, 0xE9EAEB, 0x8997A2, 0x274760, 0x486378, 0x365166, 0x204058, 0x8E9AA4, 0xEBECEC, 0xB9BABA, 0xCFD0D0}, {0xA9A9A9, 0xDCDCDC, 0xB9BEC4, 0x24445D, 0x91B2C6, 0xC7EBFD, 0x69C2D4, 0x14405C, 0x1E3F57, 0xC9CCCD, 0xDCDCDC, 0xA9A9A9}, {0x919292, 0xF2F2F2, 0x7D8D98, 0x304B5F, 0x90D5E5, 0x5DCEDD, 0x28A2D1, 0x178AC7, 0x183348, 0x8F9CA6, 0xF2F2F2, 0x919292}, {0x919292, 0xF2F2F2, 0x82909C, 0x183347, 0x228FC6, 0x209DD1, 0x1898D1, 0x0E84C6, 0x183348, 0x97A3AC, 0xEFEFEF, 0x939494}, {0xA7A8A8, 0xDDDDDD, 0xC0C5C9, 0x1E3F57, 0x0F3F5D, 0x0F83C7, 0x0B82C7, 0x0C3D5D, 0x1F3F58, 0xD9DCDE, 0xDDDDDD, 0xA7A8A8}, {0xCECECE, 0xBABBBB, 0xECECED, 0x99A3AB, 0x1D3E57, 0x18354A, 0x19344A, 0x1E3E57, 0xAEB8BF, 0xF2F2F2, 0xBABBBB, 0xCECECE}, {0xFFFFFF, 0xA2A3A3, 0xC3C3C3, 0xF0F0F1, 0xD1D5D7, 0xA6B0B9, 0xA9B4BC, 0xDCDFE2, 0xF3F3F3, 0xC3C3C3, 0xA2A3A3, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0xA2A3A3, 0xBABBBB, 0xDBDBDB, 0xF4F4F4, 0xF4F4F4, 0xDCDCDC, 0xBABBBB, 0xA2A3A3, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xCECECE, 0xAAABAB, 0x8E8F8F, 0x8E8F8F, 0xA9A9A9, 0xCECECE, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF} }; unsigned bmp_checked_highlight[12][12] = { {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xB7CCD8, 0x7FA4BA, 0x5989A5, 0x5989A5, 0x7FA4BA, 0xB7CCD8, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0x759DB4, 0x8FB7C8, 0xBCDDE5, 0xDBF6F8, 0xDBF6F8, 0xBCDDE5, 0x8FB7C8, 0x759DB4, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0x739BB3, 0x9CC2D0, 0xD3F4FA, 0x92CCED, 0x639FC7, 0x639FC7, 0x93CDED, 0xD4F4FA, 0x9CC2D0, 0x739BB3, 0xFFFFFF}, {0xB7CCD8, 0x8FB7C8, 0xCFF1FA, 0x66A3CC, 0x264862, 0x47647A, 0x355268, 0x1E405A, 0x66A3C9, 0xCFF2FA, 0x8FB7C8, 0xB7CCD8}, {0x7DA2B9, 0xBEDEE6, 0x91CCED, 0x22445E, 0x9DBBCD, 0xE9F7FE, 0x7FE6EE, 0x154664, 0x1D3F58, 0x99D3EF, 0xBEDEE6, 0x7DA2B9}, {0x5989A5, 0xDBF6F8, 0x5C98BF, 0x2F4B60, 0xB1F6FA, 0x74FFFF, 0x32CAFF, 0x1DAAF3, 0x173348, 0x6CA1C0, 0xDBF6F8, 0x5989A5}, {0x5989A5, 0xDBF6F8, 0x5E99BF, 0x173348, 0x2AB0F2, 0x28C4FF, 0x1EBEFF, 0x11A3F2, 0x173348, 0x7BA6C0, 0xD8F4F6, 0x5D8CA7}, {0x7BA1B8, 0xBFDFE7, 0x94CEEB, 0x1D3F58, 0x114567, 0x13A2F3, 0x0DA0F3, 0x0D4367, 0x1E3F58, 0xBEE0EF, 0xBFDFE7, 0x7BA1B8}, {0xB5CAD7, 0x91B8C9, 0xCFF2FA, 0x6FA8C9, 0x1C3E58, 0x18354B, 0x18354B, 0x1D3E58, 0x9CBACC, 0xDBF7FA, 0x91B8C9, 0xB5CAD7}, {0xFFFFFF, 0x739BB3, 0x9CC2D0, 0xD7F6FA, 0xAFDAED, 0x8EB3C9, 0x98B7CA, 0xC7E3EE, 0xDDF8FA, 0x9CC2D0, 0x739BB3, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0x739BB3, 0x91B8C9, 0xBCDDE5, 0xDEF9FA, 0xDEF9FA, 0xBEDEE6, 0x91B8C9, 0x739BB3, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xB5CAD7, 0x7FA4BA, 0x5586A3, 0x5586A3, 0x7DA2B9, 0xB5CAD7, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF} }; unsigned bmp_checked_press[12][12] = { {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xA6BDCE, 0x6089A8, 0x31668E, 0x31668E, 0x6089A8, 0xA6BDCE, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0x5480A1, 0x6C99B8, 0x9DC4DC, 0xBEE1F3, 0xBEE1F3, 0x9DC4DC, 0x6C99B8, 0x5480A1, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0x517E9F, 0x7AA5C2, 0xB6DDF3, 0x73B2D6, 0x4A8AB0, 0x4A8AB0, 0x74B3D8, 0xB7DEF3, 0x7AA5C2, 0x517E9F, 0xFFFFFF}, {0xA6BDCE, 0x6C99B8, 0xB1DBF1, 0x4B8DB4, 0x244660, 0x456279, 0x335167, 0x1D3F59, 0x4C90B7, 0xB1DCF3, 0x6C99B8, 0xA6BDCE}, {0x5E87A6, 0x9FC5DD, 0x71B1D6, 0x21435D, 0x7EA5BC, 0x95D9FC, 0x478BAE, 0x113858, 0x1B3E58, 0x78BDE2, 0x9FC5DD, 0x5E87A6}, {0x31668E, 0xBEE1F3, 0x4484AA, 0x2E4A60, 0x5DA2C6, 0x3A84AA, 0x19658D, 0x0F5984, 0x153248, 0x5794B7, 0xBEE1F3, 0x31668E}, {0x31668E, 0xBEE1F3, 0x4687AE, 0x153247, 0x165D84, 0x14628D, 0x0F5F8D, 0x095684, 0x163248, 0x6B9DB9, 0xBBDEF1, 0x366990}, {0x5B85A5, 0xA0C7DE, 0x74B7DC, 0x1B3E58, 0x0D3659, 0x0A5583, 0x075483, 0x0A3459, 0x1E3F58, 0xA9D2E9, 0xA0C7DE, 0x5B85A5}, {0xA3BBCD, 0x6D9BBA, 0xB2DDF3, 0x5599BE, 0x1C3E57, 0x17344A, 0x17344B, 0x1D3E57, 0x91B3C7, 0xC1E4F6, 0x6D9BBA, 0xA3BBCD}, {0xFFFFFF, 0x517E9F, 0x7AA5C2, 0xBBE1F5, 0x98CAE4, 0x80AAC3, 0x8CAFC5, 0xB7D7EA, 0xC2E4F6, 0x7AA5C2, 0x517E9F, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0x517E9F, 0x6D9BBA, 0x9DC4DC, 0xC2E4F6, 0xC2E4F6, 0x9FC5DD, 0x6D9BBA, 0x517E9F, 0xFFFFFF, 0xFFFFFF}, {0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xA3BBCD, 0x6089A8, 0x2C628B, 0x2C628B, 0x5E87A6, 0xA3BBCD, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF} }; unsigned (*colormap)[12][12] = &bmp_unchecked; switch(es) { case element_state::normal: case element_state::focus_normal: colormap = (crook_data.check_state != state::unchecked ? &bmp_checked : &bmp_unchecked); break; case element_state::hovered: case element_state::focus_hovered: colormap = (crook_data.check_state != state::unchecked ? &bmp_checked_highlight : &bmp_unchecked_highlight); break; case element_state::pressed: colormap = &bmp_checked_press; break; default: break; } const int x = r.x + 2; const int y = r.y + 2; for(int top = 0; top < 12; ++top) { for(int left = 0; left < 12; ++left) { if((*colormap)[top][left] != 0xFFFFFF) graph.set_pixel(left + x, top + y, static_cast((*colormap)[top][left])); } } } else { ::nana::color highlighted(0x5e, 0xb6, 0xf7); auto bld_bgcolor = bgcolor; auto bld_fgcolor = fgcolor; switch(es) { case element_state::hovered: case element_state::focus_hovered: bld_bgcolor = bgcolor.blend(highlighted, 0.8); bld_fgcolor = fgcolor.blend(highlighted, 0.8); break; case element_state::pressed: bld_bgcolor = bgcolor.blend(highlighted, 0.4); bld_fgcolor = fgcolor.blend(highlighted, 0.4); break; case element_state::disabled: bld_bgcolor = bld_fgcolor = nana::color(0xb2, 0xb7, 0xbc); break; default: //Leave things as they are break; } const int x = r.x + 1; const int y = r.y + 1; graph.rectangle(rectangle{ x + 1, y + 1, 11, 11 }, true, bld_bgcolor); graph.rectangle(rectangle{ x, y, 13, 13 }, false, bld_fgcolor); switch(crook_data.check_state) { case state::checked: { int sx = x + 2; int sy = y + 4; for(int i = 0; i < 3; i++) { sx++; sy++; graph.line(point{ sx, sy }, point{ sx, sy + 3 }); } for(int i = 0; i < 4; i++) { sx++; sy--; graph.line(point{ sx, sy }, point{ sx, sy + 3 }); } } break; case state::partial: graph.rectangle(rectangle{ x + 2, y + 2, 9, 9 }, true); break; default: break; } } return true; } }; //end class crook class menu_crook : public crook_interface { bool draw(graph_reference graph, const ::nana::color&, const ::nana::color& fgcolor, const nana::rectangle& r, element_state, const data& crook_data) override { if(crook_data.check_state == state::unchecked) return true; if(crook_data.radio) { unsigned colormap[8][8] = { {0xFF000000,0xdee7ef,0x737baa,0x232674,0x3c3f84,0x8d96ba,0xe0e9ef,0xFF000000}, {0xdce4ed,0x242875,0x6f71b3,0x9fa0d6,0xc3c4e9,0xb1b2da,0x5c6098,0xdbe4ed}, {0x7b81ad,0x4f5199,0x8182c1,0xa1a2d4,0xccccea,0xcccced,0x9c9dcf,0x7981ae}, {0x2b2d77,0x4f509a,0x696baf,0x7879ba,0xa4a6d4,0xa9aad9,0x9193ce,0x1e2271}, {0x36387f,0x383a87,0x52549c,0x6162a8,0x6f71b3,0x7e7fbf,0x7879ba,0x282c78}, {0x9094ba,0x1b1c71,0x3c3e8b,0x4a4b96,0x585aa1,0x6768ac,0x464893,0x828bb6}, {0xe2eaf1,0x4b4d8d,0x16186d,0x292b7c,0x333584,0x2c2e7f,0x454b8c,0xdfe9f0}, {0xFF000000,0xe4ecf2,0x9599bd,0x454688,0x414386,0x9095bb,0xe3ebf2,0xFF000000} }; int x = r.x + (static_cast(r.width) - 8) / 2; int y = r.y + (static_cast(r.height) - 8) / 2; for(int u = 0; u < 8; ++u) { for(int v = 0; v < 8; ++v) { if(colormap[u][v] & 0xFF000000) continue; graph.set_pixel(x + v, y, static_cast(colormap[u][v])); } ++y; } } else { int x = r.x + (static_cast(r.width) - 16) / 2; int y = r.y + (static_cast(r.height) - 16) / 2; graph.palette(false, fgcolor); graph.line(point{ x + 3, y + 7 }, point{ x + 6, y + 10 }); graph.line(point{ x + 7, y + 9 }, point{ x + 12, y + 4 }); graph.palette(false, fgcolor.blend(colors::white, 0.5)); graph.line(point{ x + 3, y + 8 }, point{ x + 6, y + 11 }); graph.line(point{ x + 7, y + 10 }, point{ x + 12, y + 5 }); graph.line(point{ x + 4, y + 7 }, point{ x + 6, y + 9 }); graph.line(point{ x + 7, y + 8 }, point{ x + 11, y + 4 }); } return true; } }; class border_depressed : public border_interface { bool draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color&, const ::nana::rectangle& r, element_state estate, unsigned) { graph.rectangle(r, false, static_cast((element_state::focus_hovered == estate || element_state::focus_normal == estate) ? 0x0595E2 : 0x999A9E)); graph.rectangle(::nana::rectangle(r).pare_off(1), false, bgcolor); return true; } }; class arrow_solid_triangle : public arrow_interface { bool draw(graph_reference graph, const ::nana::color&, const ::nana::color&, const ::nana::rectangle& r, element_state, direction dir) override { ::nana::point pos{ r.x + 3, r.y + 3 }; switch (dir) { case ::nana::direction::east: pos.x += 3; pos.y += 1; for (int i = 0; i < 5; ++i) graph.line(point{ pos.x + i, pos.y + i }, point{ pos.x + i, pos.y + 8 - i }); break; case ::nana::direction::south: pos.y += 3; for (int i = 0; i < 5; ++i) graph.line(point{ pos.x + i, pos.y + i }, point{ pos.x + 8 - i, pos.y + i }); break; case ::nana::direction::west: pos.x += 5; pos.y += 1; for (int i = 0; i < 5; ++i) graph.line(point{ pos.x - i, pos.y + i }, point{ pos.x - i, pos.y + 8 - i }); break; case ::nana::direction::north: pos.y += 7; for (int i = 0; i < 5; ++i) graph.line(point{ pos.x + i, pos.y - i }, point{ pos.x + 8 - i, pos.y - i }); break; case direction::southeast: pos.x += 2; pos.y += 7; for (int i = 0; i < 6; ++i) graph.line(point{ pos.x + i, pos.y - i }, point{ pos.x + 5, pos.y - i }); break; } return true; } };//end class arrow_solid_triangle class arrow_hollow_triangle : public arrow_interface { bool draw(graph_reference graph, const ::nana::color&, const ::nana::color&, const ::nana::rectangle& r, element_state, ::nana::direction dir) override { int x = r.x + 3; int y = r.y + 3; switch (dir) { case ::nana::direction::east: x += 3; graph.line(point{ x, y + 1 }, point{ x, y + 9 }); graph.line(point{ x + 1, y + 2 }, point{ x + 4, y + 5 }); graph.line(point{ x + 3, y + 6 }, point{ x + 1, y + 8 }); break; case direction::southeast: x += 7; y += 6; graph.line(point{ x - 5 , y + 1 }, point{ x, y + 1 }); graph.line(point{ x, y - 4 }, point{ x, y }); graph.line(point{ x - 4, y }, point{ x - 1, y - 3 }); break; case direction::south: y += 3; graph.line(point{ x, y }, point{ x + 8, y }); graph.line(point{ x + 1, y + 1 }, point{ x + 4, y + 4 }); graph.line(point{ x + 7, y + 1 }, point{ x + 5, y + 3 }); break; case direction::west: x += 5; y += 1; graph.line(point{ x, y }, point{ x, y + 8 }); graph.line(point{ x - 4, y + 4 }, point{ x - 1, y + 1 }); graph.line(point{ x - 3, y + 5 }, point{ x - 1, y + 7 }); break; case direction::north: y += 7; graph.line(point{ x, y }, point{ x + 8, y }); graph.line(point{ x + 1, y - 1 }, point{ x + 4, y - 4 }); graph.line(point{ x + 5, y - 3 }, point{ x + 7, y - 1 }); break; } return true; } };//end class arrow_hollow_triangle class arrowhead : public arrow_interface { bool draw(graph_reference graph, const ::nana::color&, const ::nana::color&, const ::nana::rectangle& r, element_state, ::nana::direction dir) override { int x = r.x; int y = r.y + 5; switch (dir) { case direction::north: { x += 3; int pixels = 1; for (int l = 0; l < 4; ++l) { for (int i = 0; i < pixels; ++i) { if (l == 3 && i == 3) continue; graph.set_pixel(x + i, y); } x--; y++; pixels += 2; } graph.set_pixel(x + 1, y); graph.set_pixel(x + 2, y); graph.set_pixel(x + 6, y); graph.set_pixel(x + 7, y); } break; case direction::south: { graph.set_pixel(x, y); graph.set_pixel(x + 1, y); graph.set_pixel(x + 5, y); graph.set_pixel(x + 6, y); ++y; int pixels = 7; for (int l = 0; l < 4; ++l) { for (int i = 0; i < pixels; ++i) { if (l != 0 || i != 3) graph.set_pixel(x + i, y); } x++; y++; pixels -= 2; } } default:break; } return true; } };//end class arrowhead class arrow_double : public arrow_interface { bool draw(graph_reference graph, const ::nana::color&, const ::nana::color&, const ::nana::rectangle& r, element_state, ::nana::direction dir) override { int x = r.x; int y = r.y; switch (dir) { case direction::east: _m_line(graph, x + 4, y + 6, true); _m_line(graph, x + 5, y + 7, true); _m_line(graph, x + 6, y + 8, true); _m_line(graph, x + 5, y + 9, true); _m_line(graph, x + 4, y + 10, true); break; case direction::west: _m_line(graph, x + 5, y + 6, true); _m_line(graph, x + 4, y + 7, true); _m_line(graph, x + 3, y + 8, true); _m_line(graph, x + 4, y + 9, true); _m_line(graph, x + 5, y + 10, true); break; case direction::south: _m_line(graph, x + 5, y + 4, false); _m_line(graph, x + 6, y + 5, false); _m_line(graph, x + 7, y + 6, false); _m_line(graph, x + 8, y + 5, false); _m_line(graph, x + 9, y + 4, false); break; case direction::north: _m_line(graph, x + 5, y + 6, false); _m_line(graph, x + 6, y + 5, false); _m_line(graph, x + 7, y + 4, false); _m_line(graph, x + 8, y + 5, false); _m_line(graph, x + 9, y + 6, false); break; default: break; } return true; } static void _m_line(nana::paint::graphics & graph, int x, int y, bool horizontal) { graph.set_pixel(x, y); if (horizontal) { graph.set_pixel(x + 1, y); graph.set_pixel(x + 4, y); graph.set_pixel(x + 5, y); } else { graph.set_pixel(x, y + 1); graph.set_pixel(x, y + 4); graph.set_pixel(x, y + 5); } } };//end class arrow_double class annex_button : public element_interface { bool draw(graph_reference graph, const ::nana::color& arg_bgcolor, const ::nana::color&, const rectangle& r, element_state estate) override { auto bgcolor = arg_bgcolor; switch (estate) { case element_state::hovered: case element_state::focus_hovered: bgcolor = arg_bgcolor.blend(colors::white, 0.8); break; case element_state::pressed: bgcolor = arg_bgcolor.blend(colors::black, 0.8); break; case element_state::disabled: bgcolor = colors::dark_gray; default: break; } auto part_px = (r.height - 3) * 5 / 13; graph.rectangle(r, false, bgcolor.blend(colors::black, 0.6)); ::nana::point left_top{ r.x + 1, r.y + 1 }, right_top{r.right() - 2, r.y + 1}; ::nana::point left_mid{ r.x + 1, r.y + 1 + static_cast(part_px) }, right_mid{ right_top.x, left_mid.y }; ::nana::point left_bottom{ r.x + 1, r.bottom() - 2 }, right_bottom{ r.right() - 2, r.bottom() - 2 }; graph.palette(false, bgcolor.blend(colors::white, 0.9)); graph.line(left_top, left_mid); graph.line(right_top, right_mid); graph.palette(false, bgcolor.blend(colors::white, 0.5)); graph.line(left_top, right_top); left_mid.y++; right_mid.y++; graph.palette(false, bgcolor.blend(colors::black, 0.8)); graph.line(left_mid, left_bottom); graph.line(right_mid, right_bottom); ::nana::rectangle part_r{ r.x + 2, r.y + 2, r.width - 4, part_px }; graph.rectangle(part_r, true, bgcolor.blend(colors::white, 0.8)); part_r.y += static_cast(part_r.height); part_r.height = (r.height - 3 - part_r.height); graph.rectangle(part_r, true, bgcolor); return true; } };//end class annex_button class x_icon : public element_interface { bool draw(graph_reference graph, const ::nana::color&, const ::nana::color& fgcolor, const rectangle& r, element_state estate) override { auto clr = fgcolor; switch (estate) { case element_state::hovered: case element_state::pressed: clr = clr.blend(colors::black, 0.8); break; case element_state::disabled: clr = colors::dark_gray; default: break; } graph.palette(false, clr); const int x = r.x + 4; const int y = r.y + 4; point p1{ x, y }, p2{ x + 7, y + 7 }; graph.line(p1, p2); ++p1.x; --p2.y; graph.line(p1, p2); p1.x = x; ++p1.y; p2.x = x + 6; p2.y = y + 7; graph.line(p1, p2); p1.x += 7; p1.y = y; p2.x = x; graph.line(p1, p2); p1.x = x + 6; p2.y = y + 6; graph.line(p1, p2); ++p1.x; ++p1.y; ++p2.x; ++p2.y; graph.line(p1, p2); return true; } }; }//end namespace element template class element_object : nana::noncopyable, nana::nonmovable { using element_type = ElementInterface; using factory_interface = pat::cloneable; public: ~element_object() { if(factory_) factory_->destroy(element_ptr_); } void push(const factory_interface& rhs) { auto keep_f = factory_; auto keep_e = element_ptr_; factory_ = rhs; element_ptr_ = static_cast(static_cast&>(*factory_).create()); if(nullptr == factory_ || nullptr == element_ptr_) { if(element_ptr_) factory_->destroy(element_ptr_); factory_.reset(); factory_ = keep_f; element_ptr_ = keep_e; } else spare_.emplace_back(keep_e, keep_f); } element_type * const * cite() const { return &element_ptr_; } private: factory_interface factory_; //Keep the factory for destroying the element element_type * element_ptr_{nullptr}; std::vector> spare_; }; class element_manager : nana::noncopyable, nana::nonmovable { template struct item { element_object * employee; std::map>> table; }; element_manager() { crook_.employee = nullptr; border_.employee = nullptr; } public: static element_manager& instance() { static bool initial = true; static element_manager obj; if(initial) { initial = false; element::add_crook(""); element::add_crook("menu_crook"); element::add_border(""); element::add_arrow(""); //"arrowhead" in default element::add_arrow("double"); element::add_arrow("solid_triangle"); element::add_arrow("hollow_triangle"); element::add_button(""); //"annex" in default element::add_x_icon(""); } return obj; } void crook(const std::string& name, const pat::cloneable>& factory) { _m_add(name, crook_, factory); } element::crook_interface * const * crook(const std::string& name) const { return _m_get(name, crook_).cite(); } void cross(const std::string& name, const pat::cloneable>& factory) { _m_add(name, cross_, factory); } element::element_interface* const * cross(const std::string& name) const { return _m_get(name, cross_).cite(); } void border(const std::string& name, const pat::cloneable>& factory) { _m_add(name, border_, factory); } element::border_interface * const * border(const std::string& name) const { return _m_get(name, border_).cite(); } void arrow(const std::string& name, const pat::cloneable>& factory) { _m_add((name.empty() ? "arrowhead" : name), arrow_, factory); } element::arrow_interface * const * arrow(const std::string& name) const { return _m_get((name.empty() ? "arrowhead" : name), arrow_).cite(); } void button(const std::string& name, const pat::cloneable>& factory) { _m_add((name.empty() ? "annex" : name), button_, factory); } element::element_interface * const * button(const std::string& name) const { return _m_get((name.empty() ? "annex" : name), button_).cite(); } void x_icon(const std::string& name, const pat::cloneable>& factory) { _m_add(name, x_icon_, factory); } element::element_interface * const * x_icon(const std::string& name) const { return _m_get(name, x_icon_).cite(); } private: using lock_guard = std::lock_guard; template void _m_add(const std::string& name, item& m, const pat::cloneable>& factory) { typedef element_object element_object_t; lock_guard lock(mutex_); auto & eop = m.table[name]; if(nullptr == eop) eop = std::make_shared(); eop->push(factory); if(nullptr == m.employee) m.employee = eop.get(); } template const element_object& _m_get(const std::string& name, const item& m) const { lock_guard lock(mutex_); auto i = m.table.find(name); if(i != m.table.end()) return *(i->second); return *m.employee; } private: mutable std::recursive_mutex mutex_; item crook_; item cross_; item border_; item arrow_; item button_; item x_icon_; }; namespace element { //class provider void provider::add_crook(const std::string& name, const pat::cloneable>& factory) { element_manager::instance().crook(name, factory); } crook_interface* const * provider::cite_crook(const std::string& name) { return element_manager::instance().crook(name); } void provider::add_cross(const std::string& name, const pat::cloneable>& factory) { element_manager::instance().cross(name, factory); } element_interface* const* provider::cite_cross(const std::string& name) { return element_manager::instance().cross(name); } void provider::add_border(const std::string& name, const pat::cloneable>& factory) { element_manager::instance().border(name, factory); } border_interface* const * provider::cite_border(const std::string& name) { return element_manager::instance().border(name); } void provider::add_arrow(const std::string& name, const pat::cloneable>& factory) { element_manager::instance().arrow(name, factory); } arrow_interface* const * provider::cite_arrow(const std::string& name) { return element_manager::instance().arrow(name); } void provider::add_button(const std::string& name, const pat::cloneable>& factory) { element_manager::instance().button(name, factory); } element_interface* const* provider::cite_button(const std::string& name) { return element_manager::instance().button(name); } void provider::add_x_icon(const std::string& name, const pat::cloneable>& factory) { element_manager::instance().x_icon(name, factory); } element_interface* const* provider::cite_x_icon(const std::string& name) { return element_manager::instance().x_icon(name); } }//end namespace element //facades //template<> class facade facade::facade(const char* name) : cite_(element::provider().cite_crook(name ? name : "")) { data_.check_state = state::unchecked; data_.radio = false; } facade & facade::reverse() { data_.check_state = (data_.check_state == facade::state::unchecked ? facade::state::checked : facade::state::unchecked); return *this; } facade & facade::check(state s) { data_.check_state = s; return *this; } facade::state facade::checked() const { return data_.check_state; } facade & facade::radio(bool r) { data_.radio = r; return *this; } bool facade::radio() const { return data_.radio; } void facade::switch_to(const char* name) { cite_ = element::provider().cite_crook(name ? name : ""); } bool facade::draw(graph_reference graph, const ::nana::color& bgcol, const ::nana::color& fgcol, const nana::rectangle& r, element_state es) { return (*cite_)->draw(graph, bgcol, fgcol, r, es, data_); } //end class facade //class facade facade::facade(const char* name) : cite_(element::provider().cite_cross(name ? name : "")) { } void facade::switch_to(const char* name) { cite_ = element::provider().cite_cross(name ? name : ""); } void facade::thickness(unsigned thk) { thickness_ = thk; } void facade::size(unsigned size_px) { size_ = size_px; } //Implement element_interface bool facade::draw(graph_reference graph, const ::nana::color&, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state) { if (thickness_ + 2 <= size_) { int gap = (static_cast(size_) - static_cast(thickness_)) / 2; nana::point ps[12]; ps[0].x = r.x + gap; ps[1].x = ps[0].x + static_cast(thickness_) - 1; ps[1].y = ps[0].y = r.y; ps[2].x = ps[1].x; ps[2].y = r.y + gap; ps[3].x = ps[2].x + gap; ps[3].y = ps[2].y; ps[4].x = ps[3].x; ps[4].y = ps[3].y + static_cast(thickness_)-1; ps[5].x = ps[1].x; ps[5].y = ps[4].y; ps[6].x = ps[5].x; ps[6].y = ps[5].y + gap; ps[7].x = r.x + gap; ps[7].y = ps[6].y; ps[8].x = ps[7].x; ps[8].y = ps[4].y; ps[9].x = r.x; ps[9].y = ps[4].y; ps[10].x = r.x; ps[10].y = r.y + gap; ps[11].x = r.x + gap; ps[11].y = r.y + gap; graph.palette(false, fgcolor.blend(colors::black, true)); for (int i = 0; i < 11; ++i) graph.line(ps[i], ps[i + 1]); graph.line(ps[11], ps[0]); graph.palette(false, fgcolor); unsigned thk_minus_2 = thickness_ - 2; graph.rectangle(rectangle{ ps[10].x + 1, ps[10].y + 1, (gap << 1) + thk_minus_2, thk_minus_2 }, true); graph.rectangle(rectangle{ ps[0].x + 1, ps[0].y + 1, thk_minus_2, (gap << 1) + thk_minus_2 }, true); } return true; } //end class facade //class facade facade::facade(const char* name) : cite_(element::provider().cite_border(name ? name : "")) {} void facade::switch_to(const char* name) { cite_ = element::provider().cite_border(name ? name : ""); } bool facade::draw(graph_reference graph, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle& r, element_state es) { return (*cite_)->draw(graph, bgcolor, fgcolor, r, es, 2); } //end class facade //class facade facade::facade(const char* name) : cite_(element::provider().cite_arrow(name ? name : "")) { } void facade::switch_to(const char* name) { cite_ = element::provider().cite_arrow(name ? name : ""); } void facade::direction(::nana::direction dir) { dir_ = dir; } //Implement element_interface bool facade::draw(graph_reference graph, const nana::color& bgcolor, const nana::color& fgcolor, const nana::rectangle& r, element_state estate) { graph.palette(false, fgcolor); return (*cite_)->draw(graph, bgcolor, fgcolor, r, estate, dir_); } //end class facade //class facade:: facade::facade(const char* name) : cite_(element::provider().cite_button(name ? name : "")) {} void facade::switch_to(const char* name) { cite_ = element::provider().cite_button(name ? name : ""); } //Implement element_interface bool facade::draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate) { return (*cite_)->draw(graph, bgcolor, fgcolor, r, estate); } //end class facade //class facade facade::facade(const char* name) : cite_(element::provider().cite_x_icon(name ? name : "")) {} void facade::switch_to(const char* name) { cite_ = element::provider().cite_x_icon(name ? name : ""); } //Implement element_interface bool facade::draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate) { return (*cite_)->draw(graph, bgcolor, fgcolor, r, estate); } //end class facade namespace element { using brock = ::nana::detail::bedrock; void set_bground(const char* name, const pat::cloneable& obj) { brock::instance().get_element_store().bground(name, obj); } void set_bground(const char* name, pat::cloneable && obj) { brock::instance().get_element_store().bground(name, std::move(obj)); } //class cite cite_bground::cite_bground(const char* name) : ref_ptr_(brock::instance().get_element_store().bground(name)) { } void cite_bground::set(const cloneable_element& rhs) { holder_ = rhs; place_ptr_ = holder_.get(); ref_ptr_ = &place_ptr_; } void cite_bground::set(const char* name) { holder_.reset(); ref_ptr_ = brock::instance().get_element_store().bground(name); } bool cite_bground::draw(graph_reference dst, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const nana::rectangle& r, element_state state) { if (ref_ptr_ && *ref_ptr_) return (*ref_ptr_)->draw(dst, bgcolor, fgcolor, r, state); return false; } //end class cite //class bground struct bground::draw_method { virtual ~draw_method(){} virtual draw_method * clone() const = 0; virtual void paste(const nana::rectangle& from_r, graph_reference, const nana::point& dst_pos) = 0; virtual void stretch(const nana::rectangle& from_r, graph_reference dst, const nana::rectangle & to_r) = 0; }; struct bground::draw_image : public draw_method { nana::paint::image image; draw_image(const nana::paint::image& img) : image(img) {} draw_method * clone() const override { return new draw_image(image); } void paste(const nana::rectangle& from_r, graph_reference dst, const nana::point& dst_pos) override { image.paste(from_r, dst, dst_pos); } void stretch(const nana::rectangle& from_r, graph_reference dst, const nana::rectangle& to_r) override { image.stretch(from_r, dst, to_r); } }; struct bground::draw_graph : public draw_method { nana::paint::graphics graph; draw_graph() {} draw_graph(const nana::paint::graphics& g) : graph(g) {} draw_method * clone() const override { auto p = new draw_graph; p->graph.make(graph.size()); graph.paste(p->graph, 0, 0); return p; } void paste(const nana::rectangle& from_r, graph_reference dst, const nana::point& dst_pos) override { graph.paste(from_r, dst, dst_pos.x, dst_pos.y); } void stretch(const nana::rectangle& from_r, graph_reference dst, const nana::rectangle& to_r) override { graph.stretch(from_r, dst, to_r); } }; struct bground::implementation { draw_method * method{ nullptr }; bool vert{ false }; rectangle valid_area; std::vector states; std::map join; bool stretch_all{ true }; unsigned left{ 0 }, top{ 0 }, right{ 0 }, bottom{ 0 }; }; bground::bground() : impl_{ new implementation } { reset_states(); } bground::bground(const bground& rhs) : impl_{ new implementation(*rhs.impl_) } { if (impl_->method) impl_->method = impl_->method->clone(); } bground::~bground() { delete impl_->method; } bground& bground::operator=(const bground& rhs) { if (this != &rhs) { delete impl_->method; impl_.reset(new implementation(*rhs.impl_)); if (impl_->method) impl_->method = impl_->method->clone(); } return *this; } //Set a picture for the background bground& bground::image(const paint::image& img, bool vertical, const nana::rectangle& valid_area) { delete impl_->method; impl_->method = new draw_image(img); impl_->vert = vertical; if (valid_area.width && valid_area.height) impl_->valid_area = valid_area; else impl_->valid_area = nana::rectangle(img.size()); return *this; } bground& bground::image(const paint::graphics& graph, bool vertical, const nana::rectangle& valid_area) { delete impl_->method; impl_->method = new draw_graph(graph); impl_->vert = vertical; if (valid_area.width && valid_area.height) impl_->valid_area = valid_area; else impl_->valid_area = nana::rectangle(graph.size()); return *this; } //Set the state sequence of the background picture. void bground::states(const std::vector & s) { impl_->states = s; } void bground::states(std::vector && s) { impl_->states = std::move(s); } void bground::reset_states() { auto & st = impl_->states; st.clear(); st.push_back(element_state::normal); st.push_back(element_state::hovered); st.push_back(element_state::focus_normal); st.push_back(element_state::focus_hovered); st.push_back(element_state::pressed); st.push_back(element_state::disabled); impl_->join.clear(); } void bground::join(element_state target, element_state joiner) { impl_->join[joiner] = target; } void bground::stretch_parts(unsigned left, unsigned top, unsigned right, unsigned bottom) { impl_->left = left; impl_->right = right; impl_->top = top; impl_->bottom = bottom; impl_->stretch_all = !(left || right || top || bottom); } //Implement the methods of bground_interface. bool bground::draw(graph_reference dst, const ::nana::color&, const ::nana::color&, const nana::rectangle& to_r, element_state state) { const auto method = impl_->method; if (nullptr == method) return false; auto mi = impl_->join.find(state); if (mi != impl_->join.end()) state = mi->second; std::size_t pos = 0; for (; pos < impl_->states.size(); ++pos) { if (impl_->states[pos] == state) break; } if (pos == impl_->states.size()) return false; nana::rectangle from_r = impl_->valid_area; if (impl_->vert) { from_r.height /= static_cast(impl_->states.size()); from_r.y += static_cast(from_r.height * pos); } else { from_r.width /= static_cast(impl_->states.size()); from_r.x += static_cast(from_r.width * pos); } if (impl_->stretch_all) { if (from_r.width == to_r.width && from_r.height == to_r.height) method->paste(from_r, dst, to_r.position()); else method->stretch(from_r, dst, to_r); return true; } auto perf_from_r = from_r; auto perf_to_r = to_r; const auto left = impl_->left; const auto right = impl_->right; const auto top = impl_->top; const auto bottom = impl_->bottom; if (left + right < to_r.width) { nana::rectangle src_r = from_r; src_r.y += static_cast(top); src_r.height -= top + bottom; nana::rectangle dst_r = to_r; dst_r.y += static_cast(top); dst_r.height -= top + bottom; if (left) { src_r.width = left; dst_r.width = left; method->stretch(src_r, dst, dst_r); perf_from_r.x += static_cast(left); perf_from_r.width -= left; perf_to_r.x += static_cast(left); perf_to_r.width -= left; } if (right) { src_r.x += (static_cast(from_r.width) - static_cast(right)); src_r.width = right; dst_r.x += (static_cast(to_r.width) - static_cast(right)); dst_r.width = right; method->stretch(src_r, dst, dst_r); perf_from_r.width -= right; perf_to_r.width -= right; } } if (top + bottom < to_r.height) { nana::rectangle src_r = from_r; src_r.x += static_cast(left); src_r.width -= left + right; nana::rectangle dst_r = to_r; dst_r.x += static_cast(left); dst_r.width -= left + right; if (top) { src_r.height = top; dst_r.height = top; method->stretch(src_r, dst, dst_r); perf_from_r.y += static_cast(top); perf_to_r.y += static_cast(top); } if (bottom) { src_r.y += static_cast(from_r.height - bottom); src_r.height = bottom; dst_r.y += static_cast(to_r.height - bottom); dst_r.height = bottom; method->stretch(src_r, dst, dst_r); } perf_from_r.height -= (top + bottom); perf_to_r.height -= (top + bottom); } if (left) { nana::rectangle src_r = from_r; src_r.width = left; if (top) { src_r.height = top; method->paste(src_r, dst, to_r.position()); } if (bottom) { src_r.y += static_cast(from_r.height) - static_cast(bottom); src_r.height = bottom; method->paste(src_r, dst, nana::point(to_r.x, to_r.y + static_cast(to_r.height - bottom))); } } if (right) { const int to_x = to_r.x + int(to_r.width - right); nana::rectangle src_r = from_r; src_r.x += static_cast(src_r.width) - static_cast(right); src_r.width = right; if (top) { src_r.height = top; method->paste(src_r, dst, nana::point(to_x, to_r.y)); } if (bottom) { src_r.y += (static_cast(from_r.height) - static_cast(bottom)); src_r.height = bottom; method->paste(src_r, dst, nana::point(to_x, to_r.y + int(to_r.height - bottom))); } } method->stretch(perf_from_r, dst, perf_to_r); return true; } //end class bground }//end namespace element }//end namespace nana