From 40d4bb4314267fe81254d1592d73875af5947ffb Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 29 Mar 2016 00:14:07 +0800 Subject: [PATCH] improve date_chooser add keyboard support add date_changed event --- include/nana/gui/widgets/date_chooser.hpp | 89 +- source/gui/widgets/date_chooser.cpp | 1244 +++++++++++++-------- 2 files changed, 794 insertions(+), 539 deletions(-) diff --git a/include/nana/gui/widgets/date_chooser.hpp b/include/nana/gui/widgets/date_chooser.hpp index a4e685e1..262a7a3b 100644 --- a/include/nana/gui/widgets/date_chooser.hpp +++ b/include/nana/gui/widgets/date_chooser.hpp @@ -1,7 +1,7 @@ /** * A date chooser Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -18,80 +18,47 @@ namespace nana { + class date_chooser; + + struct arg_datechooser + : public event_arg + { + date_chooser * const widget; + + arg_datechooser(date_chooser* wdg) + : widget(wdg) + {} + }; + namespace drawerbase { namespace date_chooser { + struct date_chooser_events + : public general_events + { + + basic_event date_changed; + }; + class trigger : public drawer_trigger { + class model; public: static const int topbar_height = 34; static const int border_size = 3; - enum class transform_action{none, to_left, to_right, to_enter, to_leave}; - enum class where{none, left_button, right_button, topbar, textarea}; - enum class page{date, month}; - - struct drawing_basis - { - nana::point refpos; - double line_s; - double row_s; - }; - trigger(); - bool chose() const; - nana::date read() const; - void week_name(unsigned index, const std::string&); - private: - where _m_pos_where(graph_reference, const ::nana::point& pos); - void _m_draw_topbar(graph_reference); - void _m_make_drawing_basis(drawing_basis&, graph_reference, const nana::point& refpos); - void _m_draw_pos(drawing_basis &, graph_reference, int x, int y, const ::std::string&, bool primary, bool sel); - void _m_draw_pos(drawing_basis &, graph_reference, int x, int y, int number, bool primary, bool sel); - void _m_draw_ex_days(drawing_basis &, graph_reference, int begx, int begy, bool before); - void _m_draw_days(const nana::point& refpos, graph_reference); - void _m_draw_months(const nana::point& refpos, graph_reference); - bool _m_get_trace(point, int & res); - void _m_perf_transform(transform_action tfid, graph_reference, graph_reference dirtybuf, graph_reference newbuf, const nana::point& refpos); + ~trigger(); + model* get_model() const; private: void refresh(graph_reference) override; - void attached(widget_reference, graph_reference) override; void mouse_move(graph_reference, const arg_mouse&) override; void mouse_leave(graph_reference, const arg_mouse&) override; void mouse_up(graph_reference, const arg_mouse&) override; + void key_press(graph_reference, const arg_keyboard&) override; private: - ::std::string weekstr_[7]; - - widget * widget_; - - bool chose_; - page page_; - where pos_; - nana::point trace_pos_; - - drawing_basis dbasis_; - - struct - { - int year; - int month; - int day; - }chdate_; - - struct - { - int year; - int month; - }chmonth_; - - struct color_tag - { - ::nana::color highlight; - ::nana::color selected; - ::nana::color normal; - ::nana::color bgcolor; - }color_; + model * model_; }; }//end namespace date_chooser @@ -100,18 +67,16 @@ namespace nana /// \see nana::date class date_chooser - : public widget_object + : public widget_object { public: date_chooser(); date_chooser(window, bool visible); - date_chooser(window, const ::std::string& text, bool visible = true); - date_chooser(window, const char* text, bool visible = true); date_chooser(window, const nana::rectangle& r = rectangle(), bool visible = true); bool chose() const; nana::date read() const; - void weekstr(unsigned index, const ::std::string&);///(delta * i); + dr.x = refpos.x + off_x; + dr.width = dirtybuf.width() - off_x; + + graph.bitblt(dr, dirtybuf); + + nr.width = off_x; + graph.bitblt(nr, newbuf, nana::point(static_cast(dr.width), 0)); + + API::update_window(window_handle); + nana::system::sleep(sleep_time); + } + } + else if (tfid == transform_action::to_left) + { + double delta = dirtybuf.width() / double(count); + nana::rectangle dr(refpos.x, refpos.y, 0, dirtybuf.height()); + nana::rectangle nr(0, refpos.y, 0, newbuf.height()); + + for (int i = 1; i < count; ++i) + { + int off_x = static_cast(delta * i); + dr.width = dirtybuf.width() - off_x; + + graph.bitblt(dr, dirtybuf, nana::point(off_x, 0)); + + nr.x = refpos.x + static_cast(dr.width); + nr.width = off_x; + graph.bitblt(nr, newbuf); + + API::update_window(window_handle); + nana::system::sleep(sleep_time); + } + } + else if (tfid == transform_action::to_leave) + { + nana::paint::graphics dzbuf(newbuf.size()); + nana::paint::graphics nzbuf(newbuf.size()); + + nana::rectangle r; + for (int i = 1; i < count; ++i) + { + r.width = static_cast(newbuf.width() - delta * i); + r.height = static_cast(newbuf.height() - delta_h * i); + r.x = static_cast(newbuf.width() - r.width) / 2; + r.y = static_cast(newbuf.height() - r.height) / 2; + + dzbuf.rectangle(true, colors::white); + dirtybuf.stretch(dzbuf, r); + + r.width = static_cast(newbuf.width() + delta * (count - i)); + r.height = static_cast(newbuf.height() + delta_h * (count - i)); + r.x = static_cast(newbuf.width() - r.width) / 2; + r.y = static_cast(newbuf.height() - r.height) / 2; + newbuf.stretch(nzbuf, r); + + nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); + graph.bitblt(refpos.x, refpos.y, dzbuf); + + API::update_window(window_handle); + nana::system::sleep(sleep_time); + } + } + else if (tfid == transform_action::to_enter) + { + nana::paint::graphics dzbuf(newbuf.size()); + nana::paint::graphics nzbuf(newbuf.size()); + + nana::rectangle r; + for (int i = 1; i < count; ++i) + { + r.width = static_cast(newbuf.width() + delta * i); + r.height = static_cast(newbuf.height() + delta_h * i); + r.x = static_cast(newbuf.width() - r.width) / 2; + r.y = static_cast(newbuf.height() - r.height) / 2; + dirtybuf.stretch(dzbuf, r); + + r.width = static_cast(newbuf.width() - delta * (count - i)); + r.height = static_cast(newbuf.height() - delta_h * (count - i)); + r.x = static_cast(newbuf.width() - r.width) / 2; + r.y = static_cast(newbuf.height() - r.height) / 2; + nzbuf.rectangle(true, colors::white); + newbuf.stretch(nzbuf, r); + + nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); + graph.bitblt(refpos.x, refpos.y, dzbuf); + + API::update_window(window_handle); + nana::system::sleep(sleep_time); + } + } + + graph.bitblt(nana::rectangle(refpos, newbuf.size()), newbuf); + } + + + class trigger::model + { + friend class trigger; + public: + struct view_month_rep + { + int year; + int month; + }; + + enum class where + { + none, + left_button, + right_button, + topbar, + textarea + }; + + enum class page_mode + { + date, + month + }; + + struct drawing_basis + { + nana::point refpos; + double line_s; + double row_s; + }; + + model() + { + const std::string ws[] = { "S", "M", "T", "W", "T", "F", "S" }; + for (int i = 0; i < 7; ++i) + weekstr_[i] = ws[i]; nana::date d; - chdate_.year = chmonth_.year = d.read().year; - chdate_.month = chmonth_.month = d.read().month; - chdate_.day = d.read().day; - - color_.selected = { 0x2F, 0x36, 0x99 }; - color_.highlight = { 0x4D, 0x56, 0xC8 }; - color_.normal = colors::black; - color_.bgcolor = { 0x88, 0xC4, 0xFF }; + date_.year = view_month_.year = d.read().year; + date_.month = view_month_.month = d.read().month; + date_.day = d.read().day; } - bool trigger::chose() const + void move(bool forward, graph_reference graph, window window_handle) { - return chose_; - } - - nana::date trigger::read() const - { - return nana::date(chdate_.year, chdate_.month, chdate_.day); - } - - void trigger::week_name(unsigned index, const std::string& str) - { - throw_not_utf8(str); - if(index < 7) - this->weekstr_[index] = str; - } - - trigger::where trigger::_m_pos_where(graph_reference graph, const ::nana::point& pos) - { - int xend = static_cast(graph.width()) - 1; - int yend = static_cast(graph.height()) - 1; - if(0 < pos.y && pos.y < static_cast(topbar_height)) + if (page_mode::date == page) { - if(static_cast(border_size) < pos.x && pos.x < xend) + step_view_month(forward); + + } + else + { + if (forward) + view_month_.year++; + else + view_month_.year--; + } + + perf_transform(graph, window_handle, (forward ? transform_action::to_left : transform_action::to_right)); + } + + void enter(graph_reference graph, window window_handle) + { + transform_action tfid; + + if (model::page_mode::date == page) + { + page = model::page_mode::month; + tfid = transform_action::to_leave; + + if ((!trace_.is_by_mouse) && (date_.year != view_month_.year)) { - if(pos.x < border_size + 16) + trace_.logic_pos.x = 0; + trace_.logic_pos.y = 0; + } + } + else + { + page = model::page_mode::date; + tfid = transform_action::to_enter; + + if (!trace_.is_by_mouse) + { + if ((date_.year != view_month_.year) || (date_.month != view_month_.month)) + { + trace_.logic_pos.x = 0; + trace_.logic_pos.y = 1; + } + } + } + + perf_transform(graph, window_handle, tfid); + } + + bool respond_key(graph_reference graph, const arg_keyboard& arg, bool& redrawn) + { + redrawn = false; + if (trace_.empty_logic_pos()) + return false; + + unsigned right = 7; + unsigned top = 1; + unsigned bottom = 7; + if (page_mode::month == page) + { + right = 4; + top = 0; + bottom = 3; + } + + switch (arg.key) + { + case keyboard::os_arrow_left: + if (trace_.logic_pos.x > 0) + { + --trace_.logic_pos.x; + } + else if (trace_.logic_pos.y > top) + { + trace_.logic_pos.x = right - 1; + --trace_.logic_pos.y; + } + else + { + move(false, graph, arg.window_handle); + redrawn = true; + } + break; + case keyboard::os_arrow_right: + ++trace_.logic_pos.x; + if (trace_.logic_pos.x == right) + { + if (trace_.logic_pos.y + 1 < bottom) + { + ++trace_.logic_pos.y; + trace_.logic_pos.x = 0; + } + else + { + trace_.logic_pos.x = right - 1; + move(true, graph, arg.window_handle); + redrawn = true; + } + } + + break; + case keyboard::os_arrow_up: + if (trace_.logic_pos.y == top) + trace_.logic_pos.y = bottom - 1; + else + --trace_.logic_pos.y; + break; + case keyboard::os_arrow_down: + if (trace_.logic_pos.y + 1 == bottom) + trace_.logic_pos.y = top; + else + ++trace_.logic_pos.y; + break; + case keyboard::enter: + if (page_mode::month == page) + { + int n = get_trace_logic_pos(); + view_month_.month = n; + this->enter(graph, arg.window_handle); + redrawn = true; + } + else + { + if (!trace_.empty_logic_pos()) + { + auto value = get_trace_logic_pos(); + + if (0 < value && value <= static_cast(::nana::date::month_days(view_month_.year, view_month_.month))) + this->choose(arg.window_handle, value); + else + { + move(value > 0, graph, arg.window_handle); + redrawn = true; + } + } + } + break; + case keyboard::escape: + if (page_mode::date == page) + { + enter(graph, arg.window_handle); + redrawn = true; + } + break; + default: + return false; + } + + trace_.is_by_mouse = false; + return true; + } + + ::nana::date read() const + { + return{date_.year, date_.month, date_.day}; + } + + void weekname(std::size_t pos, std::string&& name) + { + if (pos < 7) + weekstr_[pos] = std::move(name); + } + + where pos_where(const ::nana::size& area, const ::nana::point& pos) + { + int pos_right = static_cast(area.width) - 1; + int pos_bottom = static_cast(area.height) - 1; + if (0 < pos.y && pos.y < static_cast(topbar_height)) + { + if (static_cast(border_size) < pos.x && pos.x < pos_right) + { + if (pos.x < border_size + 16) return where::left_button; - else if(xend - border_size - 16 < pos.x) + else if (pos_right - border_size - 16 < pos.x) return where::right_button; return where::topbar; } } - else if(topbar_height < pos.y && pos.y < yend) + else if (topbar_height < pos.y && pos.y < pos_bottom) { - trace_pos_ = pos; + trace_.ms_pos = pos; + trace_.clear_logic_pos(); + trace_.is_by_mouse = true; + return where::textarea; } return where::none; } - void trigger::_m_draw_topbar(graph_reference graph) + bool set_where(where pos) { - ::nana::color arrow_bgcolor; - ::nana::rectangle arrow_r{ static_cast(border_size), (topbar_height - 16) / 2 + 1, 16, 16 }; - facade arrow("solid_triangle"); - arrow.direction(::nana::direction::west); - arrow.draw(graph, arrow_bgcolor, (pos_ == where::left_button ? color_.highlight : color_.normal), arrow_r, element_state::normal); + if (pos == pos_) + return false; - arrow_r.x = static_cast(graph.width()) - static_cast(border_size + 17); - arrow.direction(::nana::direction::east); - arrow.draw(graph, arrow_bgcolor, (pos_ == where::right_button ? color_.highlight : color_.normal), arrow_r, element_state::normal); - - const char * monthstr[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; - - if(graph.width() > 32 + border_size * 2) + if (pos != where::textarea) { - std::string str; - if(page_ == page::date) - { - str = ::nana::internationalization()(monthstr[chmonth_.month - 1]); - str += " "; - } - str += std::to_string(chmonth_.year); - - nana::size txt_s = graph.text_extent_size(str); - - int top = (topbar_height - static_cast(txt_s.height)) / 2 + 1; - - int xpos = static_cast(graph.width() - txt_s.width) / 2; - if(xpos < border_size + 16) xpos = 16 + border_size + 1; - - graph.string({ xpos, top }, str, (pos_ == where::topbar ? color_.highlight : color_.normal)); + trace_.clear_logic_pos(); + trace_.is_by_mouse = false; } + + pos_ = pos; + return true; } - void trigger::_m_make_drawing_basis(drawing_basis& dbasis, graph_reference graph, const nana::point& refpos) + int get_trace_logic_pos() const { - dbasis.refpos = refpos; - const unsigned width = graph.width(); - const unsigned height = graph.height(); + if (trace_.empty_logic_pos()) + return 0; - if(page::date == page_) - { - dbasis.line_s = height / 7.0; - dbasis.row_s = width / 7.0; - } - else if(page::month == page_) - { - dbasis.line_s = height / 3.0; - dbasis.row_s = width / 4.0; - } + int lines = 7, rows = 7; //for page::date - dbasis_ = dbasis; - } - - void trigger::_m_draw_pos(drawing_basis & dbasis, graph_reference graph, int x, int y, const std::string& str_utf8, bool primary, bool sel) - { - nana::rectangle r(static_cast(x * dbasis.row_s), static_cast(y * dbasis.line_s), - static_cast(dbasis.row_s), static_cast(dbasis.line_s)); - - auto color = color_.normal; - auto tpos = trace_pos_ - dbasis.refpos; - - if((pos_ == where::textarea) - && (r.x <= tpos.x) - && (tpos.x < r.x + static_cast(r.width)) - && (r.y <= tpos.y) - && (tpos.y < r.y + static_cast(r.height))) - { - if((page_ != page::date) || y) - { - color = color_.highlight; - graph.rectangle(r, true, color_.bgcolor); - } - } - - if(sel) - { - color = color_.highlight; - graph.rectangle(r, true, color_.bgcolor); - graph.rectangle(r, false, color_.selected); - } - - if(false == primary) - color = { 0xB0, 0xB0, 0xB0 }; - - auto txt_s = graph.text_extent_size(str_utf8); - graph.string({ r.x + static_cast(r.width - txt_s.width) / 2, r.y + static_cast(r.height - txt_s.height) / 2 }, str_utf8, color); - } - - void trigger::_m_draw_pos(drawing_basis & dbasis, graph_reference graph, int x, int y, int number, bool primary, bool sel) - { - _m_draw_pos(dbasis, graph, x, y, std::to_string(number), primary, sel); - } - - void trigger::_m_draw_ex_days(drawing_basis & dbasis, graph_reference graph, int begx, int begy, bool before) - { - int x = nana::date::day_of_week(chmonth_.year, chmonth_.month, 1); - int y = (x ? 1 : 2); - - if(before) - { - int year = chmonth_.year; - int month = chmonth_.month - 1; - if(month == 0) - { - --year; - month = 12; - } - bool same = (chdate_.year == year && chdate_.month == month); - int days = nana::date::month_days(year, month); - - int size = (x ? x : 7); - int beg = days - size + 1; - - for(int i = 0; i < size; ++i) - { - this->_m_draw_pos(dbasis, graph, i, 1, beg + i, false, same && (chdate_.day == beg + i)); - } - } - else - { - int year = chmonth_.year; - int month = chmonth_.month + 1; - if(month == 13) - { - ++year; - month = 1; - } - bool same = (chdate_.year == year && chdate_.month == month); - - int day = 1; - x = begx; - for(y = begy; y < 7; ++y) - { - for(; x < 7; ++x) - { - _m_draw_pos(dbasis, graph, x, y, day, false, same && (chdate_.day == day)); - ++day; - } - x = 0; - } - } - } - - void trigger::_m_draw_days(const nana::point& refpos, graph_reference graph) - { - drawing_basis dbasis; - _m_make_drawing_basis(dbasis, graph, refpos); - - for(int i = 0; i < 7; ++i) - _m_draw_pos(dbasis, graph, i, 0, weekstr_[i], true, false); - - int day = 1; - int x = nana::date::day_of_week(chmonth_.year, chmonth_.month, 1); - int y = (x ? 1 : 2); - - //draw the days that before the first day of this month - _m_draw_ex_days(dbasis, graph, 0, 0, true); - // - int days = static_cast(nana::date::month_days(chmonth_.year, chmonth_.month)); - - bool same = (chdate_.year == chmonth_.year && chdate_.month == chmonth_.month); - while(day <= days) - { - for(; x < 7; ++x) - { - _m_draw_pos(dbasis, graph, x, y, day, true, (same && chdate_.day == day)); - if(++day > days) break; - } - if(day > days) break; - y++; - x = 0; - } - - ++x; - if(x >= 7) - { - x = 0; - ++y; - } - - _m_draw_ex_days(dbasis, graph, x, y, false); - } - - void trigger::_m_draw_months(const nana::point& refpos, graph_reference graph) - { - drawing_basis dbasis; - _m_make_drawing_basis(dbasis, graph, refpos); - - const char * monthstr[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; - ::nana::internationalization i18n; - for(int y = 0; y < 3; ++y) - for(int x = 0; x < 4; ++x) - { - int index = x + y * 4; - _m_draw_pos(dbasis, graph, x, y, i18n(monthstr[index]), true, (chmonth_.year == chdate_.year) && (index + 1 == chdate_.month)); - } - } - - bool trigger::_m_get_trace(point pos, int & res) - { - pos -= dbasis_.refpos; - - int lines = 7, rows = 7; //defaultly for page::date - - if(page_ == page::month) + if (page_mode::month == page) { lines = 3; rows = 4; } - int width = static_cast(dbasis_.row_s * rows); - int height = static_cast(dbasis_.line_s * lines); - - if(0 <= pos.x && pos.x < width && 0 <= pos.y && pos.y < height) + int n = trace_.logic_pos.y * rows + trace_.logic_pos.x + 1; + if (page_mode::date == page) { - pos.x = static_cast(pos.x / dbasis_.row_s); - pos.y = static_cast(pos.y / dbasis_.line_s); + if (n < 8) return false; //Here is week title bar + int dw = nana::date::day_of_week(view_month_.year, view_month_.month, 1); + n -= (dw ? dw + 7 : 14); + } + return n; + } + + bool get_trace(point pos, int & res) const + { + pos -= dwbasis_.refpos; + + int lines = 7, rows = 7; //for page::date + + if (page_mode::month == page) + { + lines = 3; + rows = 4; + } + + int width = static_cast(dwbasis_.row_s * rows); + int height = static_cast(dwbasis_.line_s * lines); + + if (0 <= pos.x && pos.x < width && 0 <= pos.y && pos.y < height) + { + pos.x = static_cast(pos.x / dwbasis_.row_s); + pos.y = static_cast(pos.y / dwbasis_.line_s); int n = pos.y * rows + pos.x + 1; - if(page_ == page::date) + if (page_mode::date == page) { - if(n < 8) return false; //Here is week title bar - int dw = nana::date::day_of_week(chmonth_.year, chmonth_.month, 1); + if (n < 8) return false; //Here is week title bar + int dw = nana::date::day_of_week(view_month_.year, view_month_.month, 1); n -= (dw ? dw + 7 : 14); } res = n; @@ -313,291 +439,467 @@ namespace nana return false; } - void trigger::_m_perf_transform(transform_action tfid, graph_reference graph, graph_reference dirtybuf, graph_reference newbuf, const nana::point& refpos) + int days_of_view_month() const { - const int sleep_time = 15; - const int count = 20; - double delta = dirtybuf.width() / double(count); - double delta_h = dirtybuf.height() / double(count); - double fade = 1.0 / count; - - if(tfid == transform_action::to_right) - { - nana::rectangle dr(0, refpos.y, 0, dirtybuf.height()); - nana::rectangle nr(refpos.x, refpos.y, 0, newbuf.height()); - for(int i = 1; i < count; ++i) - { - int off_x = static_cast(delta * i); - dr.x = refpos.x + off_x; - dr.width = dirtybuf.width() - off_x; - - graph.bitblt(dr, dirtybuf); - - nr.width = off_x; - graph.bitblt(nr, newbuf, nana::point(static_cast(dr.width), 0)); - - API::update_window(*widget_); - nana::system::sleep(sleep_time); - } - } - else if(tfid == transform_action::to_left) - { - double delta = dirtybuf.width() / double(count); - nana::rectangle dr(refpos.x, refpos.y, 0, dirtybuf.height()); - nana::rectangle nr(0, refpos.y, 0, newbuf.height()); - - for(int i = 1; i < count; ++i) - { - int off_x = static_cast(delta * i); - dr.width = dirtybuf.width() - off_x; - - graph.bitblt(dr, dirtybuf, nana::point(off_x, 0)); - - nr.x = refpos.x + static_cast(dr.width); - nr.width = off_x; - graph.bitblt(nr, newbuf); - - API::update_window(*widget_); - nana::system::sleep(sleep_time); - } - } - else if(tfid == transform_action::to_leave) - { - nana::paint::graphics dzbuf(newbuf.size()); - nana::paint::graphics nzbuf(newbuf.size()); - - nana::rectangle r; - for(int i = 1; i < count; ++i) - { - r.width = static_cast(newbuf.width() - delta * i); - r.height = static_cast(newbuf.height() - delta_h * i); - r.x = static_cast(newbuf.width() - r.width) / 2; - r.y = static_cast(newbuf.height() - r.height) / 2; - - dzbuf.rectangle(true, colors::white); - dirtybuf.stretch(dzbuf, r); - - r.width = static_cast(newbuf.width() + delta * (count - i)); - r.height = static_cast(newbuf.height() + delta_h * (count - i)); - r.x = static_cast(newbuf.width() - r.width) / 2; - r.y = static_cast(newbuf.height() - r.height) / 2; - newbuf.stretch(nzbuf, r); - - nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); - graph.bitblt(refpos.x, refpos.y, dzbuf); - - API::update_window(*widget_); - nana::system::sleep(sleep_time); - } - } - else if(tfid == transform_action::to_enter) - { - nana::paint::graphics dzbuf(newbuf.size()); - nana::paint::graphics nzbuf(newbuf.size()); - - nana::rectangle r; - for(int i = 1; i < count; ++i) - { - r.width = static_cast(newbuf.width() + delta * i); - r.height = static_cast(newbuf.height() + delta_h * i); - r.x = static_cast(newbuf.width() - r.width) / 2; - r.y = static_cast(newbuf.height() - r.height) / 2; - dirtybuf.stretch(dzbuf, r); - - r.width = static_cast(newbuf.width() - delta * (count - i)); - r.height = static_cast(newbuf.height() - delta_h * (count - i)); - r.x = static_cast(newbuf.width() - r.width) / 2; - r.y = static_cast(newbuf.height() - r.height) / 2; - nzbuf.rectangle(true, colors::white); - newbuf.stretch(nzbuf, r); - - nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); - graph.bitblt(refpos.x, refpos.y, dzbuf); - - API::update_window(*widget_); - nana::system::sleep(sleep_time); - } - } - - graph.bitblt(nana::rectangle(refpos, newbuf.size()), newbuf); + return ::nana::date::month_days(view_month_.year, view_month_.month); } - void trigger::refresh(graph_reference graph) + void step_view_month(bool is_forward) + { + if (is_forward) + { + if (++view_month_.month == 13) + { + view_month_.month = 1; + ++view_month_.year; + } + } + else if (--view_month_.month == 0) + { + view_month_.month = 12; + --view_month_.year; + } + } + + view_month_rep& view_month() + { + return view_month_; + } + + + void set_view_month(int month) + { + view_month_.month = month; + } + + void choose(window window_handle, int day) + { + date_.year = view_month_.year; + date_.month = view_month_.month; + date_.day = day; + chose_ = true; + + arg_datechooser evt_arg{ static_cast(API::get_widget(window_handle)) }; + API::events(window_handle).date_changed.emit(evt_arg); + } + + bool chose() const + { + return chose_; + } + + void render(graph_reference graph) { const unsigned width = graph.width() - 2; - graph.rectangle(false, { 0xb0, 0xb0, 0xb0 }); + graph.rectangle(false, static_cast(0xb0b0b0)); graph.rectangle({ 1, 1, width, static_cast(topbar_height) }, true, colors::white); _m_draw_topbar(graph); if (graph.height() > 2 + topbar_height) { - nana::point refpos(1, static_cast(topbar_height)+1); + const ::nana::point refpos(1, static_cast(topbar_height)+1); + + _m_calc_basis(graph, refpos); nana::paint::graphics gbuf({ width, graph.height() - 2 - topbar_height }); gbuf.rectangle(true, { 0xf0, 0xf0, 0xf0 }); - switch (page_) + switch (page) { - case page::date: - _m_draw_days(refpos, gbuf); + case page_mode::date: + _m_draw_days(gbuf); break; - case page::month: - _m_draw_months(refpos, gbuf); + case page_mode::month: + _m_draw_months(gbuf); break; default: break; } - graph.bitblt(refpos.x, refpos.y, gbuf); } } - void trigger::attached(widget_reference widget, graph_reference) + void perf_transform(graph_reference graph, window window_handle, transform_action transf) { - widget_ = &widget; + nana::point refpos(1, static_cast(topbar_height)+1); + nana::rectangle r(0, 0, graph.width() - 2, graph.height() - 2 - topbar_height); + + nana::paint::graphics dirtybuf({ r.width, r.height }); + dirtybuf.bitblt(r, graph, refpos); + + render(graph); + + nana::paint::graphics gbuf({ r.width, r.height }); + gbuf.bitblt(r, graph, refpos); + + perf_transform_helper(window_handle, transf, graph, dirtybuf, gbuf, refpos); + } + private: + //renderring functions + + void _m_calc_basis(graph_reference graph, const nana::point& refpos) + { + dwbasis_.refpos = refpos; + unsigned width = graph.width() - 2; + unsigned height = graph.height() - 2; + + if (height < topbar_height) + height = 0; + else + height -= topbar_height; + + if (page_mode::date == page) + { + dwbasis_.line_s = height / 7.0; + dwbasis_.row_s = width / 7.0; + } + else if (page_mode::month == page) + { + dwbasis_.line_s = height / 3.0; + dwbasis_.row_s = width / 4.0; + } + } + + void _m_draw_topbar(graph_reference graph) + { + ::nana::color arrow_bgcolor; + ::nana::rectangle arrow_r{ static_cast(border_size), (topbar_height - 16) / 2 + 1, 16, 16 }; + facade arrow("solid_triangle"); + arrow.direction(::nana::direction::west); + arrow.draw(graph, arrow_bgcolor, (pos_ == where::left_button ? colors_.highlighted : colors_.normal), arrow_r, element_state::normal); + + arrow_r.x = static_cast(graph.width()) - static_cast(border_size + 17); + arrow.direction(::nana::direction::east); + arrow.draw(graph, arrow_bgcolor, (pos_ == where::right_button ? colors_.highlighted : colors_.normal), arrow_r, element_state::normal); + + const char * monthstr[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + + if (graph.width() > 32 + border_size * 2) + { + std::string str; + if (page_mode::date == page) + { + str = ::nana::internationalization()(monthstr[this->view_month_.month - 1]); + str += " "; + } + str += std::to_string(view_month_.year); + + nana::size txt_s = graph.text_extent_size(str); + + int top = (topbar_height - static_cast(txt_s.height)) / 2 + 1; + + int xpos = static_cast(graph.width() - txt_s.width) / 2; + if (xpos < border_size + 16) xpos = 16 + border_size + 1; + + graph.string({ xpos, top }, str, (pos_ == where::topbar ? colors_.highlighted : colors_.normal)); + } + } + + void _m_draw_pos(graph_reference graph, const upoint& logic_pos, const std::string& text_utf8, bool primary, bool sel) + { + nana::rectangle r(static_cast(logic_pos.x * dwbasis_.row_s), static_cast(logic_pos.y * dwbasis_.line_s), + static_cast(dwbasis_.row_s), static_cast(dwbasis_.line_s)); + + auto color = colors_.normal; + + if (trace_.is_by_mouse ? ((pos_ == where::textarea) && r.is_hit(trace_.ms_pos - dwbasis_.refpos)) : (trace_.logic_pos == logic_pos)) + { + if ((page != page_mode::date) || logic_pos.y) + { + color = colors_.highlighted; + graph.rectangle(r, true, colors_.bgcolor); + + if (trace_.is_by_mouse) + trace_.logic_pos = logic_pos; + } + } + + if (sel) + { + color = colors_.highlighted; + graph.rectangle(r, true, colors_.bgcolor); + graph.rectangle(r, false, colors_.selected); + + if (trace_.empty_logic_pos()) + trace_.logic_pos = logic_pos; + } + + if (false == primary) + color = { 0xB0, 0xB0, 0xB0 }; + + auto txt_s = graph.text_extent_size(text_utf8); + graph.string({ r.x + static_cast(r.width - txt_s.width) / 2, r.y + static_cast(r.height - txt_s.height) / 2 }, text_utf8, color); + } + + void _m_draw_ex_days(graph_reference graph, const upoint& begin_logic_pos, bool before) + { + int x = nana::date::day_of_week(view_month_.year, view_month_.month, 1); + int y = (x ? 1 : 2); + + int year = view_month_.year; + if (before) + { + + int month = view_month_.month - 1; + if (month == 0) + { + --year; + month = 12; + } + bool same = (date_.year == year && date_.month == month); + auto days = nana::date::month_days(year, month); + + unsigned size = (x ? x : 7); + unsigned beg = days - size + 1; + + for (upoint logic_pos{0, 1}; logic_pos.x < size; ++ logic_pos.x) + { + this->_m_draw_pos(graph, logic_pos, std::to_string(beg + logic_pos.x), false, same && (static_cast(date_.day) == beg + logic_pos.x)); + } + } + else + { + int month = view_month_.month + 1; + if (month == 13) + { + ++year; + month = 1; + } + bool same = (date_.year == year && date_.month == month); + + int day = 1; + + for (upoint logic_pos{begin_logic_pos}; logic_pos.y < 7; ++logic_pos.y) + { + for (; logic_pos.x < 7; ++logic_pos.x) + { + _m_draw_pos(graph, logic_pos, std::to_string(day), false, same && (date_.day == day)); + ++day; + } + logic_pos.x = 0; + } + } + } + + void _m_draw_days(graph_reference graph) + { + upoint logic_pos{ 0, 0 }; + for (; logic_pos.x < 7; ++logic_pos.x) + _m_draw_pos(graph, logic_pos, weekstr_[logic_pos.x], true, false); + + //draw the days that before the first day of this month + _m_draw_ex_days(graph, {}, true); + + int days = static_cast(nana::date::month_days(view_month_.year, view_month_.month)); + + bool same = (date_.year == view_month_.year && date_.month == view_month_.month); + + logic_pos.x = ::nana::date::day_of_week(view_month_.year, view_month_.month, 1); + logic_pos.y = (logic_pos.x ? 1 : 2); + + int day = 1; + while (day <= days) + { + for (; logic_pos.x < 7; ++logic_pos.x) + { + _m_draw_pos(graph, logic_pos, std::to_string(day), true, (same && date_.day == day)); + if (++day > days) break; + } + if (day > days) break; + logic_pos.y++; + logic_pos.x = 0; + } + + ++logic_pos.x; + if (logic_pos.x >= 7) + { + logic_pos.x = 0; + ++logic_pos.y; + } + + _m_draw_ex_days(graph, logic_pos, false); + } + + void _m_draw_months(graph_reference graph) + { + const char * monthstr[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + ::nana::internationalization i18n; + + for (unsigned y = 0; y < 3; ++y) + for (upoint logic_pos{0, y}; logic_pos.x < 4; ++logic_pos.x) + { + int index = logic_pos.x + logic_pos.y * 4; + _m_draw_pos(graph, logic_pos, i18n(monthstr[index]), true, (view_month_.year == date_.year) && (index + 1 == date_.month)); + } + } + private: + page_mode page{ page_mode::date }; + private: + ::std::string weekstr_[7]; + + bool chose_{ false }; //indicates whether the date is chose + where pos_{ where::none }; + + struct + { + bool is_by_mouse{ true }; + point ms_pos; //the mouse position + upoint logic_pos{ 8, 8 }; //pos(8, 8) indicates the end position. + + void clear_logic_pos() + { + logic_pos.x = logic_pos.y = 8; + } + + bool empty_logic_pos() const + { + return (logic_pos.x == 8); + } + }trace_; + + drawing_basis dwbasis_; + + struct color_rep + { + ::nana::color highlighted{ static_cast(0x4d56c8) }; + ::nana::color selected{ static_cast(0x2f3699) }; + ::nana::color normal{ colors::black }; + ::nana::color bgcolor{ static_cast(0x88c4ff) }; + }colors_; + + view_month_rep view_month_; + + struct date_mode + { + int year; + int month; + int day; + }date_; + }; + //class trigger: public drawer_trigger + + trigger::trigger() + : model_(new model) + { + } + + trigger::~trigger() + { + delete model_; + } + + auto trigger::get_model() const -> model* + { + return model_; + } + + void trigger::refresh(graph_reference graph) + { + model_->render(graph); } void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { - where pos = _m_pos_where(graph, arg.pos); - if(pos == pos_ && pos_ != where::textarea) return; - pos_ = pos; - refresh(graph); - API::lazy_refresh(); + auto pos = model_->pos_where(graph.size(), arg.pos); + if (model_->set_where(pos) || (model::where::textarea == pos)) + { + model_->render(graph); + API::lazy_refresh(); + } } void trigger::mouse_leave(graph_reference graph, const arg_mouse&) { - if(where::none == pos_) return; - pos_ = where::none; - refresh(graph); - API::lazy_refresh(); + if (model_->set_where(model::where::none)) + { + model_->render(graph); + API::lazy_refresh(); + } } void trigger::mouse_up(graph_reference graph, const arg_mouse& arg) { bool redraw = true; - where pos = _m_pos_where(graph, arg.pos); - transform_action tfid = transform_action::none; + auto pos = model_->pos_where(graph.size(), arg.pos); + //transform_action tfid = transform_action::none; + bool transformed = false; - if(pos == where::topbar) + if (pos == model::where::topbar) { - switch(page_) + if (model::page_mode::date == model_->page) { - case page::date: - page_ = page::month; - tfid = transform_action::to_leave; - break; - default: - redraw = false; + model_->enter(graph, arg.window_handle); + transformed = true; } + else + redraw = false; } - else if(pos == where::textarea) + else if (pos == model::where::textarea) { int ret = 0; - switch(page_) + + bool good_trace = model_->get_trace(arg.pos, ret); + + switch (model_->page) { - case page::date: - if(_m_get_trace(arg.pos, ret)) + case model::page_mode::date: + if (good_trace) { - if(ret < 1) + if (ret < 1) { - if(--chmonth_.month == 0) - { - --chmonth_.year; - chmonth_.month = 12; - } - tfid = transform_action::to_right; + model_->move(false, graph, arg.window_handle); + transformed = true; } else { - int days = nana::date::month_days(chmonth_.year, chmonth_.month); - if(ret > days) + auto days = model_->days_of_view_month(); + if (ret > days) { - if(++chmonth_.month == 13) - { - ++chmonth_.year; - chmonth_.month = 1; - } - tfid = transform_action::to_left; + model_->move(true, graph, arg.window_handle); + transformed = true; } else //Selecting a day in this month { - chdate_.year = chmonth_.year; - chdate_.month = chmonth_.month; - chdate_.day = ret; - chose_ = true; + model_->choose(arg.window_handle, ret); } } } break; - case page::month: - if(_m_get_trace(arg.pos, ret)) - chmonth_.month = ret; - page_ = page::date; - tfid = transform_action::to_enter; + case model::page_mode::month: + if (good_trace) + model_->view_month().month = ret; + + model_->enter(graph, arg.window_handle); + transformed = true; break; default: redraw = false; } } - else if(pos == where::left_button || pos == where::right_button) + else if (model::where::left_button == pos || model::where::right_button == pos) { - int end_m; - int beg_m; - int step; - if(pos == where::left_button) - { - end_m = 1; - beg_m = 12; - step = -1; - tfid = transform_action::to_right; - } - else - { - end_m = 12; - beg_m = 1; - step = 1; - tfid = transform_action::to_left; - } - switch(page_) - { - case page::date: - if(chmonth_.month == end_m) - { - chmonth_.month = beg_m; - chmonth_.year += step; - } - else - chmonth_.month += step; - break; - case page::month: - chmonth_.year += step; - break; - default: - redraw = false; - } + model_->move((model::where::right_button == pos), graph, arg.window_handle); + transformed = true; } + else + redraw = false; - if(redraw) + if (redraw) { - if(tfid != transform_action::none) - { - nana::point refpos(1, static_cast(topbar_height) + 1); - nana::rectangle r(0, 0, graph.width() - 2, graph.height() - 2 - topbar_height); + if (!transformed) + model_->render(graph); - nana::paint::graphics dirtybuf({ r.width, r.height }); - dirtybuf.bitblt(r, graph, refpos); + API::lazy_refresh(); + } + } - refresh(graph); - - nana::paint::graphics gbuf({ r.width, r.height }); - gbuf.bitblt(r, graph, refpos); - - _m_perf_transform(tfid, graph, dirtybuf, gbuf, refpos); - } - else + void trigger::key_press(graph_reference graph, const arg_keyboard& arg) + { + bool redrawn = false; + if (model_->respond_key(graph, arg, redrawn)) + { + if (!redrawn) refresh(graph); API::lazy_refresh(); @@ -616,18 +918,6 @@ namespace nana create(wd, rectangle(), visible); } - date_chooser::date_chooser(window wd, const std::string& text, bool visible) - { - throw_not_utf8(text); - create(wd, rectangle(), visible); - caption(text); - } - - date_chooser::date_chooser(window wd, const char* text, bool visible) - : date_chooser(wd, std::string(text), visible) - { - } - date_chooser::date_chooser(window wd, const rectangle& r, bool visible) { create(wd, r, visible); @@ -635,17 +925,17 @@ namespace nana bool date_chooser::chose() const { - return get_drawer_trigger().chose(); + return get_drawer_trigger().get_model()->chose(); } nana::date date_chooser::read() const { - return get_drawer_trigger().read(); + return get_drawer_trigger().get_model()->read(); } - void date_chooser::weekstr(unsigned index, const ::std::string& str) + void date_chooser::weekstr(unsigned index, ::std::string str) { - get_drawer_trigger().week_name(index, str); + get_drawer_trigger().get_model()->weekname(index, std::move(str)); API::refresh_window(*this); } //end class date_chooser