diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index c0ef0aa5..789ab5c3 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -84,6 +84,7 @@ namespace nana end_of_medium = 0x19, //Ctrl+Y substitute = 0x1A, //Ctrl+Z escape = 0x1B, + space = 0x20, //Space //The following names are intuitive name of ASCII control codes select_all = start_of_headline, diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index 659f4316..c4945552 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -118,9 +118,11 @@ namespace detail bool is_ancestor_of(const basic_window* wd) const; bool visible_parents() const; + bool displayed() const; bool belong_to_lazy() const; + const basic_window * child_caret() const; //Returns a child which owns a caret - bool is_draw_through() const; ///< Determines whether it is a draw-through window. + bool is_draw_through() const; // Determines whether it is a draw-through window. public: //Override event_holder bool set_events(const std::shared_ptr&) override; diff --git a/include/nana/gui/detail/drawer.hpp b/include/nana/gui/detail/drawer.hpp index 121c84b8..fa62974b 100644 --- a/include/nana/gui/detail/drawer.hpp +++ b/include/nana/gui/detail/drawer.hpp @@ -23,12 +23,18 @@ namespace nana { class widget; + namespace detail + { + class drawer; + } + class drawer_trigger : ::nana::noncopyable, ::nana::nonmovable { + friend class detail::drawer; public: - typedef widget& widget_reference; - typedef paint::graphics& graph_reference; + using widget_reference = widget&; + using graph_reference = paint::graphics&; virtual ~drawer_trigger(); virtual void attached(widget_reference, graph_reference); //none-const @@ -56,10 +62,11 @@ namespace nana virtual void key_release(graph_reference, const arg_keyboard&); virtual void shortkey(graph_reference, const arg_keyboard&); - void _m_reset_overrided(); - bool _m_overrided() const; private: - bool overrided_{false}; + void _m_reset_overrided(); + bool _m_overrided(event_code) const; + private: + unsigned overrided_{ 0xFFFFFFFF }; }; namespace detail @@ -83,7 +90,7 @@ namespace nana enum class method_state { - unknown, + pending, overrided, not_overrided }; @@ -128,31 +135,27 @@ namespace nana template void _m_emit(event_code evt_code, const Arg& arg, Mfptr mfptr) { - if (realizer_) + const int pos = static_cast(evt_code); + if (realizer_ && (method_state::not_overrided != mth_state_[pos])) { - const int pos = static_cast(evt_code); - if (method_state::not_overrided != mth_state_[pos]) + _m_bground_pre(); + + if (method_state::pending == mth_state_[pos]) { - _m_bground_pre(); + (realizer_->*mfptr)(graphics, arg); + + //Check realizer, when the window is closed in that event handler, the drawer will be + //detached and realizer will be a nullptr + if(realizer_) + mth_state_[pos] = (realizer_->_m_overrided(evt_code) ? method_state::overrided : method_state::not_overrided); + } + else + (realizer_->*mfptr)(graphics, arg); - if (method_state::unknown == mth_state_[pos]) - { - realizer_->_m_reset_overrided(); - (realizer_->*mfptr)(graphics, arg); - - //Check realizer, when the window is closed in that event handler, the drawer will be - //detached and realizer will be a nullptr - if(realizer_) - mth_state_[pos] = (realizer_->_m_overrided() ? method_state::overrided : method_state::not_overrided); - } - else - (realizer_->*mfptr)(graphics, arg); - - if (_m_lazy_decleared()) - { - _m_draw_dynamic_drawing_object(); - _m_bground_end(); - } + if (_m_lazy_decleared()) + { + _m_draw_dynamic_drawing_object(); + _m_bground_end(); } } } diff --git a/include/nana/gui/detail/effects_renderer.hpp b/include/nana/gui/detail/effects_renderer.hpp index b9b1b785..6b189d84 100644 --- a/include/nana/gui/detail/effects_renderer.hpp +++ b/include/nana/gui/detail/effects_renderer.hpp @@ -23,7 +23,7 @@ namespace nana{ return object; } - std::size_t weight() const + unsigned weight() const { return 2; } @@ -55,22 +55,23 @@ namespace nana{ } } - bool render(core_window_t * wd, bool forced, const rectangle* update_area = nullptr) + void render(core_window_t * wd, bool forced, const rectangle* update_area = nullptr) { - bool rendered = false; - core_window_t * root_wd = wd->root_widget; - auto & nimbus = root_wd->other.attribute.root->effects_edge_nimbus; + bool copy_separately = true; + std::vector> rd_set; - if(nimbus.size()) + if (wd->root_widget->other.attribute.root->effects_edge_nimbus.size()) { - core_window_t * focused = root_wd->other.attribute.root->focus; - native_window_type native = root_wd->root; - std::size_t pixels = weight(); + auto root_wd = wd->root_widget; + + auto & nimbus = root_wd->other.attribute.root->effects_edge_nimbus; + + auto focused = root_wd->other.attribute.root->focus; + + const unsigned pixels = weight(); auto graph = root_wd->root_graph; - std::vector erase; - std::vector> rd_set; nana::rectangle r; for(auto & action : nimbus) { @@ -80,11 +81,11 @@ namespace nana{ { if (update_area) ::nana::overlap(*update_area, rectangle(r), r); - rendered = true; + copy_separately = false; } //Avoiding duplicated rendering. If the window is declared to lazy refresh, it should be rendered. - if ((forced && (action.window == wd)) || !action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh)) + if ((forced && (action.window == wd)) || (focused == action.window) || !action.rendered || (action.window->other.upd_state == core_window_t::update_state::refresh)) { rd_set.emplace_back(r, action.window); action.rendered = true; @@ -93,29 +94,36 @@ namespace nana{ else if(action.rendered) { action.rendered = false; - erase.push_back(action.window); + + if (action.window == wd) + copy_separately = false; + + ::nana::rectangle erase_r( + action.window->pos_root.x - static_cast(pixels), + action.window->pos_root.y - static_cast(pixels), + static_cast(action.window->dimension.width + (pixels << 1)), + static_cast(action.window->dimension.height + (pixels << 1)) + ); + + graph->paste(root_wd->root, erase_r, erase_r.x, erase_r.y); } } - - //Erase - for(auto el : erase) - { - if(el == wd) - rendered = true; - - r.x = el->pos_root.x - static_cast(pixels); - r.y = el->pos_root.y - static_cast(pixels); - r.width = static_cast(el->dimension.width + (pixels << 1)); - r.height = static_cast(el->dimension.height + (pixels << 1)); - - graph->paste(native, r, r.x, r.y); - } - - //Render - for (auto & rd : rd_set) - _m_render_edge_nimbus(rd.second, rd.first); } - return rendered; + + if (copy_separately) + { + rectangle vr; + if (window_layer::read_visual_rectangle(wd, vr)) + { + if (update_area) + ::nana::overlap(*update_area, rectangle(vr), vr); + wd->root_graph->paste(wd->root, vr, vr.x, vr.y); + } + } + + //Render + for (auto & rd : rd_set) + _m_render_edge_nimbus(rd.second, rd.first); } private: static bool _m_edge_nimbus(core_window_t * focused_wd, core_window_t * wd) @@ -134,8 +142,8 @@ namespace nana{ nana::rectangle good_r; if(overlap(r, wd->root_graph->size(), good_r)) { - if( (good_r.x < wd->pos_root.x) || (good_r.y < wd->pos_root.y) || - (good_r.x + good_r.width > visual.x + visual.width) || (good_r.y + good_r.height > visual.y + visual.height)) + if ((good_r.x < wd->pos_root.x) || (good_r.y < wd->pos_root.y) || + (good_r.right() > visual.right()) || (good_r.bottom() > visual.bottom())) { auto graph = wd->root_graph; nana::paint::pixel_buffer pixbuf(graph->handle(), r); diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index 96aec1fa..27a6316c 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -109,7 +109,7 @@ namespace nana if(real_visible_state_ != isshow) { real_visible_state_ = isshow; - native_interface::caret_visible(wd_->root, isshow); + native_interface::caret_visible(wd_->root, isshow && wd_->displayed()); } } @@ -282,6 +282,11 @@ namespace nana return true; } + bool basic_window::displayed() const + { + return (visible && visible_parents()); + } + bool basic_window::belong_to_lazy() const { for (auto wd = this; wd; wd = wd->parent) @@ -292,6 +297,26 @@ namespace nana return false; } + const basic_window* get_child_caret(const basic_window* wd, bool this_is_a_child) + { + if (this_is_a_child && wd->together.caret) + return wd; + + for (auto child : wd->children) + { + auto caret_wd = get_child_caret(child, true); + if (caret_wd) + return caret_wd; + } + + return nullptr; + } + + const basic_window * basic_window::child_caret() const + { + return get_child_caret(this, false); + } + bool basic_window::is_draw_through() const { if (::nana::category::flags::root == this->other.category) diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index e76b0686..fc41706c 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -63,15 +63,16 @@ namespace nana arg.window_handle = reinterpret_cast(wd); if (emit(event_code::expose, wd, arg, false, get_thread_context())) { - if (wd->together.caret) + const core_window_t * caret_wd = (wd->together.caret ? wd : wd->child_caret()); + if (caret_wd) { if (exposed) { - if (wd->root_widget->other.attribute.root->focus == wd) - wd->together.caret->visible(true); + if (wd->root_widget->other.attribute.root->focus == caret_wd) + caret_wd->together.caret->visible(true); } else - wd->together.caret->visible(false); + caret_wd->together.caret->visible(false); } if (!exposed) @@ -102,7 +103,7 @@ namespace nana arg.x = x; arg.y = y; if (emit(event_code::move, wd, arg, false, get_thread_context())) - wd_manager.update(wd, true, true); + wd_manager.update(wd, false, true); } } diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index 0170ec0d..f1308677 100644 --- a/source/gui/detail/drawer.cpp +++ b/source/gui/detail/drawer.cpp @@ -34,99 +34,99 @@ namespace nana void drawer_trigger::resizing(graph_reference, const arg_resizing&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::resizing)); } void drawer_trigger::resized(graph_reference graph, const arg_resized&) { - overrided_ = true; + overrided_ |= (1 << static_cast(event_code::resized)); this->refresh(graph); detail::bedrock::instance().thread_context_lazy_refresh(); } void drawer_trigger::move(graph_reference, const arg_move&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::move)); } void drawer_trigger::click(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::click)); } void drawer_trigger::dbl_click(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::dbl_click)); } void drawer_trigger::mouse_enter(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_enter)); } void drawer_trigger::mouse_move(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_move)); } void drawer_trigger::mouse_leave(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_leave)); } void drawer_trigger::mouse_down(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_down)); } void drawer_trigger::mouse_up(graph_reference, const arg_mouse&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_up)); } void drawer_trigger::mouse_wheel(graph_reference, const arg_wheel&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_wheel)); } void drawer_trigger::mouse_dropfiles(graph_reference, const arg_dropfiles&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::mouse_drop)); } void drawer_trigger::focus(graph_reference, const arg_focus&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::focus)); } void drawer_trigger::key_press(graph_reference, const arg_keyboard&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::key_press)); } void drawer_trigger::key_char(graph_reference, const arg_keyboard&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::key_char)); } void drawer_trigger::key_release(graph_reference, const arg_keyboard&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::key_release)); } void drawer_trigger::shortkey(graph_reference, const arg_keyboard&) { - overrided_ = false; + overrided_ &= ~(1 << static_cast(event_code::shortkey)); } void drawer_trigger::_m_reset_overrided() { - overrided_ = true; + overrided_ = 0xFFFFFFFF; } - bool drawer_trigger::_m_overrided() const + bool drawer_trigger::_m_overrided(event_code evt_code) const { - return overrided_; + return 0 != (overrided_ & (1 << static_cast(evt_code))); } //end class drawer_trigger @@ -244,8 +244,8 @@ namespace nana { if(wd) { - bedrock_type::core_window_t* iwd = reinterpret_cast(wd); - bedrock_type::core_window_t * caret_wd = iwd->root_widget->other.attribute.root->focus; + auto iwd = reinterpret_cast(wd); + auto caret_wd = iwd->root_widget->other.attribute.root->focus; bool owns_caret = (caret_wd && (caret_wd->together.caret) && (caret_wd->together.caret->visible())); @@ -262,16 +262,7 @@ namespace nana #endif } - if (false == edge_nimbus_renderer_t::instance().render(iwd, forced, update_area)) - { - rectangle vr; - if (bedrock_type::window_manager_t::window_layer::read_visual_rectangle(iwd, vr)) - { - if (update_area) - ::nana::overlap(*update_area, rectangle(vr), vr); - iwd->root_graph->paste(iwd->root, vr, vr.x, vr.y); - } - } + edge_nimbus_renderer_t::instance().render(iwd, forced, update_area); if(owns_caret) { @@ -306,9 +297,10 @@ namespace nana void drawer::attached(widget& wd, drawer_trigger& realizer) { for (auto i = std::begin(mth_state_), end = std::end(mth_state_); i != end; ++i) - *i = method_state::unknown; + *i = method_state::pending; realizer_ = &realizer; + realizer._m_reset_overrided(); realizer.attached(wd, graphics); } diff --git a/source/gui/detail/linux_X11/bedrock.cpp b/source/gui/detail/linux_X11/bedrock.cpp index d484cbb1..65414360 100644 --- a/source/gui/detail/linux_X11/bedrock.cpp +++ b/source/gui/detail/linux_X11/bedrock.cpp @@ -937,11 +937,10 @@ namespace detail if(msgwnd->visible && (msgwnd->root_graph->empty() == false)) { nana::detail::platform_scope_guard psg; - nana::detail::drawable_impl_type* drawer_impl = msgwnd->root_graph->handle(); - ::XCopyArea(display, drawer_impl->pixmap, reinterpret_cast(native_window), drawer_impl->context, - xevent.xexpose.x, xevent.xexpose.y, - xevent.xexpose.width, xevent.xexpose.height, - xevent.xexpose.x, xevent.xexpose.y); + //Don't copy root_graph to the window directly, otherwise the edge nimbus effect will be missed. + ::nana::rectangle update_area(xevent.xexpose.x, xevent.xexpose.y, xevent.xexpose.width, xevent.xexpose.height); + if (!update_area.empty()) + msgwnd->drawer.map(reinterpret_cast(msgwnd), true, &update_area); } break; case KeyPress: @@ -1149,7 +1148,9 @@ namespace detail brock.get_key_state(arg); brock.emit(event_code::key_release, msgwnd, arg, true, &context); } - brock.delay_restore(2); //Restores while key release + + if (context.platform.keychar < keyboard::os_arrow_left || keyboard::os_arrow_down < wParam) + brock.delay_restore(2); //Restores while key release } else { diff --git a/source/gui/detail/win32/bedrock.cpp b/source/gui/detail/win32/bedrock.cpp index 8cd5231c..65e29071 100644 --- a/source/gui/detail/win32/bedrock.cpp +++ b/source/gui/detail/win32/bedrock.cpp @@ -363,7 +363,7 @@ namespace detail void bedrock::pump_event(window modal_window, bool is_modal) { const unsigned tid = ::GetCurrentThreadId(); - thread_context * context = this->open_thread_context(tid); + auto context = this->open_thread_context(tid); if(0 == context->window_count) { //test if there is not a window @@ -1310,13 +1310,11 @@ namespace detail ::PAINTSTRUCT ps; ::HDC dc = ::BeginPaint(root_window, &ps); - if((ps.rcPaint.left != ps.rcPaint.right) && (ps.rcPaint.bottom != ps.rcPaint.top)) - { - ::BitBlt(dc, - ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, - reinterpret_cast(msgwnd->root_graph->handle()->context), - ps.rcPaint.left, ps.rcPaint.top, SRCCOPY); - } + //Don't copy root_graph to the window directly, otherwise the edge nimbus effect will be missed. + ::nana::rectangle update_area(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top); + if (!update_area.empty()) + msgwnd->drawer.map(reinterpret_cast(msgwnd), true, &update_area); + ::EndPaint(root_window, &ps); } break; @@ -1474,7 +1472,10 @@ namespace detail else brock.set_keyboard_shortkey(false); - brock.delay_restore(2); //Restores while key release + //Do delay restore if key is not arrow_left/right/up/down, otherwise + //A menubar will be restored if the item is empty(not have a menu item) + if (wParam < 37 || 40 < wParam) + brock.delay_restore(2); //Restores while key release break; case WM_CLOSE: { diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index a77eff5f..5734acbe 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -59,7 +59,6 @@ namespace nana if (wd->parent) { std::vector blocks; - blocks.reserve(10); if (read_overlaps(wd, vr, blocks)) { nana::point p_src; @@ -99,7 +98,7 @@ namespace nana // The result is a rectangle that is a visible area for its ancesters. bool window_layout::read_visual_rectangle(core_window_t* wd, nana::rectangle& visual) { - if (false == wd->visible) return false; + if (! wd->displayed()) return false; visual = rectangle{ wd->pos_root, wd->dimension }; @@ -354,7 +353,7 @@ namespace nana nana::rectangle r_of_sigwd(sigwd->pos_root, sigwd->dimension); for (auto wd : data_sect.effects_bground_windows) { - if (wd == sigwd || !wd->visible || !wd->visible_parents() || + if (wd == sigwd || !wd->displayed() || (false == overlap(nana::rectangle{ wd->pos_root, wd->dimension }, r_of_sigwd))) continue; diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 30c497fa..8a952bf2 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -698,7 +698,7 @@ namespace detail std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd) == false) return false; - if (wd->visible && wd->visible_parents()) + if (wd->displayed()) { if(forced || (false == wd->belong_to_lazy())) { @@ -722,7 +722,7 @@ namespace detail std::lock_guard lock(mutex_); //It's not worthy to redraw if visible is false - if (impl_->wd_register.available(wd) && wd->visible && wd->visible_parents()) + if (impl_->wd_register.available(wd) && wd->displayed()) window_layer::paint(wd, true, true); } @@ -1060,7 +1060,7 @@ namespace detail bool precondition = false; for (auto & tab_wd : tabs) { - if (tab_wd->visible) + if (tab_wd->displayed()) { precondition = true; break; @@ -1073,7 +1073,7 @@ namespace detail while (new_stop && (wd != new_stop)) { - if (new_stop->flags.enabled && new_stop->visible) + if (new_stop->flags.enabled && new_stop->displayed()) return new_stop; new_stop = get_tabstop(new_stop, forward); diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index 63d8e810..9e660203 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -151,7 +151,7 @@ namespace nana{ namespace drawerbase void trigger::key_char(graph_reference, const arg_keyboard& arg) { - if(arg.key == static_cast(keyboard::enter)) + if (static_cast(keyboard::enter) == arg.key || static_cast(keyboard::space) == arg.key) emit_click(); } diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 8b2af38f..1a1a7aa3 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -3512,6 +3512,7 @@ namespace nana m.flags.checked = ck; arg_listbox arg{*this, ck}; ess_->lister.wd_ptr()->events().checked.emit(arg); + ess_->update(); } return *this; } @@ -3539,6 +3540,7 @@ namespace nana else if (ess_->lister.last_selected_abs == pos_) ess_->lister.last_selected_abs.set_both(npos); + ess_->update(); return *this; } diff --git a/source/gui/widgets/menu.cpp b/source/gui/widgets/menu.cpp index 807d2b75..054e639a 100644 --- a/source/gui/widgets/menu.cpp +++ b/source/gui/widgets/menu.cpp @@ -114,7 +114,7 @@ namespace nana { if(at.item_state == state::active) { - graph.rectangle(r, false, {0xa8, 0xd8, 0xeb}); + graph.rectangle(r, false, static_cast(0xa8d8eb)); nana::point points[4] = { nana::point(r.x, r.y), nana::point(r.x + r.width - 1, r.y), @@ -200,35 +200,35 @@ namespace nana void checked(std::size_t index, bool check) { - if(root_.items.size() > index) + if (root_.items.size() <= index) + return; + + item_type & m = root_.items[index]; + if(check && (checks::option == m.style)) { - item_type & m = root_.items[index]; - if(check && (checks::option == m.style)) + if(index) { - if(index) + std::size_t i = index; + do { - std::size_t i = index; - do - { - item_type& el = root_.items[--i]; - if(el.flags.splitter) break; - - if(checks::option == el.style) - el.flags.checked = false; - }while(i); - } - - for(std::size_t i = index + 1; i < root_.items.size(); ++i) - { - item_type & el = root_.items[i]; + item_type& el = root_.items[--i]; if(el.flags.splitter) break; if(checks::option == el.style) el.flags.checked = false; - } + }while(i); + } + + for(std::size_t i = index + 1; i < root_.items.size(); ++i) + { + item_type & el = root_.items[i]; + if(el.flags.splitter) break; + + if(checks::option == el.style) + el.flags.checked = false; } - m.flags.checked = check; } + m.flags.checked = check; } menu_type& data() @@ -304,7 +304,7 @@ namespace nana : public drawer_trigger { public: - typedef menu_item_type::item_proxy item_proxy; + using item_proxy = menu_item_type::item_proxy; renderer_interface * renderer; @@ -330,12 +330,12 @@ namespace nana detail_.monitor_pos = API::cursor_position(); } - void mouse_move(graph_reference, const arg_mouse& arg) + void mouse_move(graph_reference graph, const arg_mouse& arg) { state_.nullify_mouse = false; - if(track_mouse(arg.pos.x, arg.pos.y)) + if(track_mouse(arg.pos)) { - draw(); + refresh(graph); API::lazy_refresh(); } } @@ -350,9 +350,70 @@ namespace nana state_.nullify_mouse = false; } - void refresh(graph_reference) + void refresh(graph_reference graph) { - draw(); + if (nullptr == menu_) return; + + _m_adjust_window_size(); + + renderer->background(graph, *widget_); + + const unsigned item_h_px = _m_item_height(); + const unsigned image_px = item_h_px - 2; + nana::rectangle item_r(2, 2, graph_->width() - 4, item_h_px); + + unsigned strpixels = item_r.width - 60; + + int text_top_off = (item_h_px - graph.text_extent_size(STR("jh({[")).height) / 2; + + std::size_t pos = 0; + for (auto & m : menu_->items) + { + if (m.flags.splitter) + { + graph_->set_color(colors::gray_border); + graph_->line({ item_r.x + 40, item_r.y }, { static_cast(graph.width()) - 1, item_r.y }); + item_r.y += 2; + ++pos; + continue; + } + + renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, m); + //Draw item background + renderer->item(*graph_, item_r, attr); + + //Draw text, the text is transformed from orignal for hotkey character + nana::char_t hotkey; + nana::string::size_type hotkey_pos; + nana::string text = API::transform_shortkey_text(m.text, hotkey, &hotkey_pos); + + if (m.image.empty() == false) + renderer->item_image(graph, nana::point(item_r.x + 5, item_r.y + static_cast(item_h_px - image_px) / 2 - 1), image_px, m.image); + + renderer->item_text(graph, nana::point(item_r.x + 40, item_r.y + text_top_off), text, strpixels, attr); + + if (hotkey) + { + m.hotkey = hotkey; + if (m.flags.enabled) + { + unsigned off_w = (hotkey_pos ? graph.text_extent_size(text, static_cast(hotkey_pos)).width : 0); + nana::size hotkey_size = graph.text_extent_size(text.c_str() + hotkey_pos, 1); + int x = item_r.x + 40 + off_w; + int y = item_r.y + text_top_off + hotkey_size.height; + + graph_->set_color(colors::black); + graph_->line({ x, y }, { x + static_cast(hotkey_size.width) - 1, y }); + } + } + + if (m.sub_menu) + renderer->sub_arrow(graph, nana::point(graph_->width() - 20, item_r.y), item_h_px, attr); + + item_r.y += item_r.height + 1; + + ++pos; + } } std::size_t active() const @@ -411,21 +472,21 @@ namespace nana state_.active = pos; state_.sub_window = false; - draw(); + refresh(*graph_); return true; } return false; } - bool track_mouse(int x, int y) + bool track_mouse(const ::nana::point& pos) { - if(state_.nullify_mouse == false) + if (!state_.nullify_mouse) { - std::size_t index = _m_get_index_by_pos(x, y); - if(index != state_.active) + std::size_t index = _m_get_index_by_pos(pos.x, pos.y); + if (index != state_.active) { - if((index == npos) && menu_->items.at(state_.active).sub_menu && state_.sub_window) + if ((index == npos) && menu_->items.at(state_.active).sub_menu && state_.sub_window) return false; state_.active = (index != npos && menu_->items.at(index).flags.splitter) ? npos : index; @@ -433,6 +494,7 @@ namespace nana return true; } } + return false; } @@ -451,29 +513,35 @@ namespace nana state_.sub_window = subw; } - menu_type* retrive_sub_menu(nana::point& pos, std::size_t interval) const + menu_type* get_sub(nana::point& pos, unsigned long& tmstamp) const { - if(state_.active != npos && (nana::system::timestamp() - state_.active_timestamp >= interval)) + if (npos == state_.active) + return nullptr; + + auto sub = menu_->items.at(state_.active).sub_menu; + if (sub) { - pos.x = graph_->width() - 2; + pos.x = static_cast(graph_->width()) - 2; pos.y = 2; - std::size_t index = 0; - for(auto & m : menu_->items) + auto index = state_.active; + for (auto & m : menu_->items) { - if(false == m.flags.splitter) + if (m.flags.splitter) { - if(index == state_.active) - break; - - pos.y += _m_item_height() + 1; - } - else pos.y += 2; + continue; + } - ++index; + if (0 == index) + break; + + pos.y += _m_item_height() + 1; + --index; } - return (menu_->items.at(state_.active).sub_menu); + + tmstamp = state_.active_timestamp; + return sub; } return nullptr; } @@ -498,8 +566,7 @@ namespace nana state_.active = index; state_.active_timestamp = nana::system::timestamp(); - draw(); - API::update_window(*widget_); + API::refresh_window(*widget_); return 2; } else if(m.flags.enabled) @@ -514,72 +581,6 @@ namespace nana } return 0; } - - void draw() const - { - if(nullptr == menu_) return; - - _m_adjust_window_size(); - - renderer->background(*graph_, *widget_); - - const unsigned item_h_px = _m_item_height(); - const unsigned image_px = item_h_px - 2; - nana::rectangle item_r(2, 2, graph_->width() - 4, item_h_px); - - unsigned strpixels = item_r.width - 60; - - int text_top_off = (item_h_px - graph_->text_extent_size(STR("jh({[")).height) / 2; - - std::size_t pos = 0; - for(auto & m : menu_->items) - { - if(m.flags.splitter) - { - graph_->set_color(colors::gray_border); - graph_->line({ item_r.x + 40, item_r.y }, { static_cast(graph_->width()) - 1, item_r.y }); - item_r.y += 2; - ++pos; - continue; - } - - renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, m); - //Draw item background - renderer->item(*graph_, item_r, attr); - - //Draw text, the text is transformed from orignal for hotkey character - nana::char_t hotkey; - nana::string::size_type hotkey_pos; - nana::string text = API::transform_shortkey_text(m.text, hotkey, &hotkey_pos); - - if(m.image.empty() == false) - renderer->item_image(*graph_, nana::point(item_r.x + 5, item_r.y + static_cast(item_h_px - image_px) / 2 - 1), image_px, m.image); - - renderer->item_text(*graph_, nana::point(item_r.x + 40, item_r.y + text_top_off), text, strpixels, attr); - - if(hotkey) - { - m.hotkey = hotkey; - if(m.flags.enabled) - { - unsigned off_w = (hotkey_pos ? graph_->text_extent_size(text, static_cast(hotkey_pos)).width : 0); - nana::size hotkey_size = graph_->text_extent_size(text.c_str() + hotkey_pos, 1); - int x = item_r.x + 40 + off_w; - int y = item_r.y + text_top_off + hotkey_size.height; - - graph_->set_color(colors::black); - graph_->line({ x, y }, { x + static_cast(hotkey_size.width) - 1, y }); - } - } - - if(m.sub_menu) - renderer->sub_arrow(*graph_, nana::point(graph_->width() - 20, item_r.y), item_h_px, attr); - - item_r.y += item_r.height + 1; - - ++pos; - } - } private: static renderer_interface::attr _m_make_renderer_attr(bool active, const menu_item_type & m) { @@ -684,10 +685,10 @@ namespace nana struct state { - std::size_t active; - unsigned long active_timestamp; - unsigned long sub_window: 1; - unsigned long nullify_mouse: 1; + std::size_t active; + unsigned active_timestamp; + bool sub_window: 1; + bool nullify_mouse: 1; }state_; struct widget_detail @@ -722,7 +723,19 @@ namespace nana submenu_.child = submenu_.parent = nullptr; submenu_.object = nullptr; - _m_make_mouse_event(); + state_.mouse_pos = API::cursor_position(); + events().mouse_move.connect_unignorable([this]{ + nana::point pos = API::cursor_position(); + if (pos != state_.mouse_pos) + { + menu_window * root = this; + while (root->submenu_.parent) + root = root->submenu_.parent; + root->state_.auto_popup_submenu = true; + + state_.mouse_pos = pos; + } + }); } void popup(menu_type& menu, bool owner_menubar) @@ -749,13 +762,19 @@ namespace nana _m_key_down(arg); }); - events().mouse_up.connect_unignorable([this]{ - pick(); + events().mouse_down.connect_unignorable([this](const arg_mouse& arg) + { + this->_m_open_sub(0); //Try to open submenu immediately + }); + + events().mouse_up.connect_unignorable([this](const arg_mouse& arg){ + if (arg.left_button) + pick(); }); timer_.interval(100); timer_.elapse([this]{ - this->_m_check_repeatly(); + this->_m_open_sub(500); //Try to open submenu }); timer_.start(); @@ -801,29 +820,27 @@ namespace nana bool submenu(bool enter) { - menu_window * object = this; - while (object->submenu_.child) - object = object->submenu_.child; + menu_window * menu_wd = this; + while (menu_wd->submenu_.child) + menu_wd = menu_wd->submenu_.child; state_.auto_popup_submenu = false; - if (enter) + if (!enter) { - if (object->submenu_.parent) + if (menu_wd->submenu_.parent) { - auto & sub = object->submenu_.parent->submenu_; + auto & sub = menu_wd->submenu_.parent->submenu_; sub.child = nullptr; sub.object = nullptr; - object->close(); + menu_wd->close(); return true; } return false; } - nana::point pos; - menu_type * sbm = object->get_drawer_trigger().retrive_sub_menu(pos, 0); - return object->_m_show_submenu(sbm, pos, true); + return menu_wd->_m_manipulate_sub(0, true); } int send_shortkey(nana::char_t key) @@ -969,63 +986,51 @@ namespace nana } } - void _m_make_mouse_event() + bool _m_manipulate_sub(unsigned long delay_ms, bool forced) { - state_.mouse_pos = API::cursor_position(); - events().mouse_move.connect_unignorable([this]{ - _m_mouse_event(); - }); - } + auto & drawer = get_drawer_trigger(); + ::nana::point pos; + unsigned long tmstamp; - void _m_mouse_event() - { - nana::point pos = API::cursor_position(); - if(pos != state_.mouse_pos) + auto menu_ptr = drawer.get_sub(pos, tmstamp); + + if (menu_ptr == submenu_.object) + return false; + + if (menu_ptr && (::nana::system::timestamp() - tmstamp < delay_ms)) + return false; + + if (submenu_.object && (menu_ptr != submenu_.object)) { - menu_window * root = this; - while(root->submenu_.parent) - root = root->submenu_.parent; - root->state_.auto_popup_submenu = true; - - state_.mouse_pos = pos; - } - } - - bool _m_show_submenu(menu_type* sbm, nana::point pos, bool forced) - { - auto & mdtrigger = get_drawer_trigger(); - if(submenu_.object && (sbm != submenu_.object)) - { - mdtrigger.set_sub_window(false); + drawer.set_sub_window(false); submenu_.child->close(); submenu_.child = nullptr; submenu_.object = nullptr; } - if(sbm) + if (menu_ptr) { menu_window * root = this; - while(root->submenu_.parent) + while (root->submenu_.parent) root = root->submenu_.parent; - if((submenu_.object == nullptr) && sbm && (forced || root->state_.auto_popup_submenu)) + if ((submenu_.object == nullptr) && menu_ptr && (forced || root->state_.auto_popup_submenu)) { - sbm->item_pixels = mdtrigger.data()->item_pixels; - sbm->gaps = mdtrigger.data()->gaps; - pos.x += sbm->gaps.x; - pos.y += sbm->gaps.y; + menu_ptr->item_pixels = drawer.data()->item_pixels; + menu_ptr->gaps = drawer.data()->gaps; + pos += menu_ptr->gaps; - menu_window & mwnd = form_loader()(handle(), true, pos, mdtrigger.renderer); + menu_window & mwnd = form_loader()(handle(), true, pos, drawer.renderer); mwnd.state_.self_submenu = true; - submenu_.child = & mwnd; + submenu_.child = &mwnd; submenu_.child->submenu_.parent = this; - submenu_.object = sbm; + submenu_.object = menu_ptr; API::set_window_z_order(handle(), mwnd.handle(), z_order_action::none); - mwnd.popup(*sbm, state_.owner_menubar); - mdtrigger.set_sub_window(true); - if(forced) + mwnd.popup(*menu_ptr, state_.owner_menubar); + drawer.set_sub_window(true); + if (forced) mwnd.goto_next(true); return true; @@ -1034,17 +1039,16 @@ namespace nana return false; } - void _m_check_repeatly() + void _m_open_sub(unsigned delay_ms) //check_repeatly { if(state_.auto_popup_submenu) { - nana::point pos = API::cursor_position(); + auto pos = API::cursor_position(); - drawer_type& drawer = get_drawer_trigger(); API::calc_window_point(handle(), pos); - drawer.track_mouse(pos.x, pos.y); - menu_type* sbm = drawer.retrive_sub_menu(pos, 500); - _m_show_submenu(sbm, pos, false); + get_drawer_trigger().track_mouse(pos); + + _m_manipulate_sub(delay_ms, false); } } private: