From eeaaf07f4c8e6f1a4fa25cadb0796eaf016e8b49 Mon Sep 17 00:00:00 2001 From: PeterAddy960 Date: Wed, 31 Aug 2016 20:29:34 -0500 Subject: [PATCH 01/16] Added methods to font object to get if font has underline and strikeout. --- source/paint/graphics.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index 40530a18..ea8212cb 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -156,6 +156,18 @@ namespace paint return (impl_->font_ptr->italic); } + bool font::underline() const + { + if(empty()) return false; + return (impl_->font_ptr->underline); + } + + bool font::strikeout() const + { + if(empty()) return false; + return (impl_->font_ptr->strikeout); + } + native_font_type font::handle() const { if(empty()) return nullptr; From f4443bba903f84ea1ba8ea3f2bcd06addbc50b11 Mon Sep 17 00:00:00 2001 From: PeterAddy960 Date: Wed, 31 Aug 2016 20:30:11 -0500 Subject: [PATCH 02/16] Added methods to font object to get if font has underline and strikeout. --- include/nana/paint/graphics.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/nana/paint/graphics.hpp b/include/nana/paint/graphics.hpp index 8e667ef2..af4e1bdc 100644 --- a/include/nana/paint/graphics.hpp +++ b/include/nana/paint/graphics.hpp @@ -52,6 +52,8 @@ namespace nana bool italic() const; native_font_type handle() const; void release(); + bool strikeout() const; + bool underline() const; font& operator=(const font&); bool operator==(const font&) const; From 4b587041618bbbca550821363c844c53a5054219 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 1 Oct 2016 10:07:13 +0800 Subject: [PATCH 03/16] fix issue when textbox::reset with no parameter --- include/nana/gui/widgets/skeletons/text_editor.hpp | 2 +- source/gui/widgets/skeletons/text_editor.cpp | 9 +++++---- source/gui/widgets/textbox.cpp | 9 ++++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index b4f6318d..c2050a66 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -148,7 +148,7 @@ namespace nana{ namespace widgets * @param reset indicates whether to reset the text position by the pos. If this parameter is true, the text position is set by pos. If the parameter is false, it only moves the UI caret to the specified position. */ bool move_caret(const upoint& pos, bool reset = false); - void move_caret_end(); + void move_caret_end(bool update); void reset_caret_pixels() const; void reset_caret(); void show_caret(bool isshow); diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 28429f8f..00dd0d71 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1797,7 +1797,7 @@ namespace nana{ namespace widgets if (select_all) { select(true); - move_caret_end(); + move_caret_end(false); renderred = true; //If the text widget is focused by clicking mouse button, the selected text will be cancelled @@ -1991,8 +1991,6 @@ namespace nana{ namespace widgets return str; } - //move_caret - //Set caret position through text coordinate bool text_editor::move_caret(const upoint& crtpos, bool reset_caret) { const unsigned line_pixels = line_height(); @@ -2036,11 +2034,14 @@ namespace nana{ namespace widgets return false; } - void text_editor::move_caret_end() + void text_editor::move_caret_end(bool update) { points_.caret.y = static_cast(impl_->textbase.lines()); if(points_.caret.y) --points_.caret.y; points_.caret.x = static_cast(impl_->textbase.getline(points_.caret.y).size()); + + if (update) + this->move_caret(points_.caret, false); } void text_editor::reset_caret_pixels() const diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 2501e541..67d6436c 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -260,8 +260,11 @@ namespace drawerbase { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); if (editor) - { - editor->text(to_wstring(str), end_caret); + { + editor->text(to_wstring(str), false); + + if (end_caret) + editor->move_caret_end(true); editor->textbase().reset(); API::update_window(this->handle()); @@ -358,7 +361,7 @@ namespace drawerbase { if(editor) { if(at_caret == false) - editor->move_caret_end(); + editor->move_caret_end(false); editor->put(to_wstring(text)); API::update_window(this->handle()); From 7c36c27d4639e08c2014ec605f28b33c878a6fdb Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 11 Oct 2016 02:53:41 +0800 Subject: [PATCH 04/16] fix group bgcolor issue --- source/gui/detail/window_manager.cpp | 2 +- source/gui/widgets/group.cpp | 37 ++++++++++++++-------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 63e30e48..5cfe1d10 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -1048,7 +1048,7 @@ namespace detail { //The window is still mapped because of edge nimbus effect. //Avoid duplicate copy if action state is not changed and the window is not focused. - if ((wd->flags.action != wd->flags.action_before) || (bedrock::instance().focus() == wd)) + if (wd->flags.action != wd->flags.action_before) this->map(wd, true); } } diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index 91b1bd9a..615bfac2 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -1,7 +1,7 @@ /** * A group widget implementation * Nana C++ Library(http://www.nanaro.org) - * Copyright(C) 2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2015-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -36,6 +36,8 @@ namespace nana{ unsigned gap{2}; std::string usr_div_str; + nana::size caption_dimension; + std::vector> options; radio_group * radio_logic{nullptr}; @@ -60,19 +62,18 @@ namespace nana{ void update_div() { - ::nana::size sz = caption.measure(1000); + caption_dimension = caption.measure(1000); - std::stringstream ss; - ss << "vert margin=[0," << gap << "," << gap + 5 << "," << gap << "]" - << " <" << field_title << " weight=" << sz.width + 1 << "> >" - << "<"; + std::string div = "vert margin=[0," + std::to_string(gap) + "," + std::to_string(gap + 5) + "," + std::to_string(gap) + "]"; + div += "<" + field_title + " weight=" + std::to_string(caption_dimension.width + 1) + ">>"; + div += "<"; if (!usr_div_str.empty()) - ss << "<" << usr_div_str << ">>"; + div += "<" + usr_div_str + ">>"; else - ss << ">"; + div += ">"; - place_content.div(ss.str().c_str()); + place_content.div(div.c_str()); if (options.empty()) place_content.field_display(field_options, false); @@ -202,25 +203,23 @@ namespace nana{ outter.collocate(); color pbg = API::bgcolor(this->parent()); - impl_->caption.bgcolor(pbg.blend(colors::black, 0.975)); - color bg = pbg.blend(colors::black, 0.950); - bgcolor(bg); + impl_->caption.bgcolor(pbg.blend(colors::black, 0.975)); + + this->bgcolor(pbg.blend(colors::black, 0.950)); drawing dw(*this); - ::nana::size sz = impl_->caption.measure(1000); - // This drawing function is owner by the onwer of dw (the outer panel of the group widget), not by dw !! - dw.draw([this, sz, bg, pbg](paint::graphics& graph) + dw.draw([this](paint::graphics& graph) { auto gap_px = impl_->gap - 1; - graph.rectangle(true, pbg); - graph.round_rectangle(rectangle(point(gap_px, sz.height / 2), - nana::size(graph.width() - 2 * gap_px, graph.height() - sz.height / 2 - gap_px) + graph.rectangle(true, API::bgcolor(this->parent())); + graph.round_rectangle(rectangle(point(gap_px, impl_->caption_dimension.height / 2), + nana::size(graph.width() - 2 * gap_px, graph.height() - impl_->caption_dimension.height / 2 - gap_px) ), - 3, 3, colors::gray_border, true, bg); + 3, 3, colors::gray_border, true, this->bgcolor()); }); } From 092ae97c66ddc675067cee98ed9be6e928c9baf5 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 14 Oct 2016 07:19:56 +0800 Subject: [PATCH 05/16] fix transparent window bugs --- include/nana/gui/detail/basic_window.hpp | 2 +- include/nana/gui/detail/effects_renderer.hpp | 2 +- include/nana/gui/detail/window_layout.hpp | 21 +++++---- include/nana/gui/detail/window_manager.hpp | 2 +- source/gui/detail/basic_window.cpp | 2 +- source/gui/detail/bedrock_posix.cpp | 12 +---- source/gui/detail/bedrock_windows.cpp | 12 +---- source/gui/detail/drawer.cpp | 2 +- source/gui/detail/window_layout.cpp | 46 ++++++++++---------- source/gui/detail/window_manager.cpp | 33 +++++++++----- 10 files changed, 64 insertions(+), 70 deletions(-) diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index 038acdbb..a21ef4ad 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -85,7 +85,7 @@ namespace detail enum class update_state { - none, lazy, refresh + none, lazy, refreshed, request_refresh }; struct edge_nimbus_action diff --git a/include/nana/gui/detail/effects_renderer.hpp b/include/nana/gui/detail/effects_renderer.hpp index 155fcb78..3727933a 100644 --- a/include/nana/gui/detail/effects_renderer.hpp +++ b/include/nana/gui/detail/effects_renderer.hpp @@ -86,7 +86,7 @@ namespace nana{ } //Avoiding duplicated rendering. If the window is declared to lazy refresh, it should be rendered. - if ((forced && (action.window == wd)) || (focused == action.window) || !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::refreshed)) { rd_set.emplace_back(r, action.window); action.rendered = true; diff --git a/include/nana/gui/detail/window_layout.hpp b/include/nana/gui/detail/window_layout.hpp index 34987291..da099cf5 100644 --- a/include/nana/gui/detail/window_layout.hpp +++ b/include/nana/gui/detail/window_layout.hpp @@ -1,6 +1,6 @@ /* * Window Layout Implementation - * Copyright(C) 2003-2013 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 @@ -42,10 +42,16 @@ namespace detail core_window_t * window; rectangle r; }; - public: - static void paint(core_window_t*, bool is_redraw, bool is_child_refreshed); - static bool maproot(core_window_t*, bool have_refreshed, bool is_child_refreshed); + enum class paint_operation { + none, + have_refreshed, + try_refresh + }; + public: + static void paint(core_window_t*, paint_operation, bool request_refresh_children); + + static bool maproot(core_window_t*, bool have_refreshed, bool request_refresh_children); static void paste_children_to_graphics(core_window_t*, nana::paint::graphics& graph); @@ -68,13 +74,12 @@ namespace detail //_m_paste_children //@brief:paste children window to the root graphics directly. just paste the visual rectangle - static void _m_paste_children(core_window_t*, bool is_child_refreshed, bool have_refreshed, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos); + static void _m_paste_children(core_window_t*, bool have_refreshed, bool request_refresh_children, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos); static void _m_paint_glass_window(core_window_t*, bool is_redraw, bool is_child_refreshed, bool called_by_notify, bool notify_other); - //_m_notify_glasses - //@brief: Notify the glass windows that are overlapped with the specified vis_rect - static void _m_notify_glasses(core_window_t* const sigwd, const nana::rectangle& r_visual); + //Notify the windows which have brground to update their background buffer. + static void _m_notify_glasses(core_window_t* const sigwd); private: struct data_section { diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index 1548b79f..e3491ac0 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -124,7 +124,7 @@ namespace detail bool update(core_window_t*, bool redraw, bool force, const rectangle* update_area = nullptr); void refresh_tree(core_window_t*); - bool do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false); + void do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false); bool get_graphics(core_window_t*, nana::paint::graphics&); bool get_visual_rectangle(core_window_t*, nana::rectangle&); diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index 3f5849cc..67b24139 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -323,7 +323,7 @@ namespace nana { for (auto wd = this; wd; wd = wd->parent) { - if (basic_window::update_state::refresh == wd->other.upd_state) + if (basic_window::update_state::refreshed == wd->other.upd_state) return true; } return false; diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 26de1dbc..6e6602d9 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -1203,17 +1203,7 @@ namespace detail { thread_context* thrd = get_thread_context(0); if(thrd && thrd->event_window) - { - //the state none should be tested, becuase in an event, there would be draw after an update, - //if the none is not tested, the draw after update will not be refreshed. - switch(thrd->event_window->other.upd_state) - { - case core_window_t::update_state::none: - case core_window_t::update_state::lazy: - thrd->event_window->other.upd_state = core_window_t::update_state::refresh; - default: break; - } - } + thrd->event_window->other.upd_state = core_window_t::update_state::refresh; } //Dynamically set a cursor for a window diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 300866f0..a790a0c2 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -1691,17 +1691,7 @@ namespace detail { auto* thrd = get_thread_context(0); if (thrd && thrd->event_window) - { - //the state none should be tested, becuase in an event, there would be draw after an update, - //if the none is not tested, the draw after update will not be refreshed. - switch (thrd->event_window->other.upd_state) - { - case core_window_t::update_state::none: - case core_window_t::update_state::lazy: - thrd->event_window->other.upd_state = core_window_t::update_state::refresh; - default: break; - } - } + thrd->event_window->other.upd_state = core_window_t::update_state::refreshed; } //Dynamically set a cursor for a window diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index 0ab725aa..0c69bee3 100644 --- a/source/gui/detail/drawer.cpp +++ b/source/gui/detail/drawer.cpp @@ -391,7 +391,7 @@ namespace nana bool drawer::_m_lazy_decleared() const { - return (basic_window::update_state::refresh == data_impl_->window_handle->other.upd_state); + return (basic_window::update_state::refreshed == data_impl_->window_handle->other.upd_state); } drawer::method_state& drawer::_m_mth_state(int pos) diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index ec69baa3..86439dec 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -22,26 +22,26 @@ namespace nana namespace detail { //class window_layout - void window_layout::paint(core_window_t* wd, bool is_redraw, bool is_child_refreshed) + void window_layout::paint(core_window_t* wd, paint_operation operation, bool req_refresh_children) { - if (is_redraw && wd->flags.refreshing) + if (wd->flags.refreshing && (paint_operation::try_refresh == operation)) return; if (nullptr == wd->effect.bground) { - if (is_redraw && (!wd->drawer.graphics.empty())) + if ((paint_operation::try_refresh == operation) && (!wd->drawer.graphics.empty())) { wd->flags.refreshing = true; wd->drawer.refresh(); wd->flags.refreshing = false; } - maproot(wd, is_redraw, is_child_refreshed); + maproot(wd, (paint_operation::none != operation), req_refresh_children); } else - _m_paint_glass_window(wd, is_redraw, is_child_refreshed, false, true); + _m_paint_glass_window(wd, (paint_operation::try_refresh == operation), req_refresh_children, false, true); } - bool window_layout::maproot(core_window_t* wd, bool have_refreshed, bool is_child_refreshed) + bool window_layout::maproot(core_window_t* wd, bool have_refreshed, bool req_refresh_children) { auto check_opaque = wd->seek_non_lite_widget_ancestor(); if (check_opaque && check_opaque->flags.refreshing) @@ -56,7 +56,7 @@ namespace nana if (category::flags::lite_widget != wd->other.category) graph.bitblt(vr, wd->drawer.graphics, nana::point(vr.x - wd->pos_root.x, vr.y - wd->pos_root.y)); - _m_paste_children(wd, is_child_refreshed, have_refreshed, vr, graph, nana::point()); + _m_paste_children(wd, have_refreshed, req_refresh_children, vr, graph, nana::point()); if (wd->parent) { @@ -81,11 +81,11 @@ namespace nana graph.bitblt(el.r, (el.window->drawer.graphics), p_src); } - _m_paste_children(el.window, is_child_refreshed, false, el.r, graph, nana::point{}); + _m_paste_children(el.window, false, req_refresh_children, el.r, graph, nana::point{}); } } } - _m_notify_glasses(wd, vr); + _m_notify_glasses(wd); return true; } return false; @@ -255,8 +255,8 @@ namespace nana if (category::flags::lite_widget != child->other.category) glass_buffer.bitblt(nana::rectangle{ ovlp.x - wd->pos_owner.x, ovlp.y - wd->pos_owner.y, ovlp.width, ovlp.height }, child->drawer.graphics, {ovlp.position() - child->pos_owner}); - ovlp.x += wd->pos_root.x; - ovlp.y += wd->pos_root.y; + ovlp.x += wd->parent->pos_root.x; + ovlp.y += wd->parent->pos_root.y; _m_paste_children(child, false, false, ovlp, glass_buffer, rpos); } } @@ -267,7 +267,7 @@ namespace nana //_m_paste_children //@brief:paste children window to the root graphics directly. just paste the visual rectangle - void window_layout::_m_paste_children(core_window_t* wd, bool is_child_refreshed, bool have_refreshed, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos) + void window_layout::_m_paste_children(core_window_t* wd, bool have_refreshed, bool req_refresh_children, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos) { nana::rectangle rect; for (auto child : wd->children) @@ -278,7 +278,7 @@ namespace nana if (category::flags::root == child->other.category) { - paint(child, is_child_refreshed, is_child_refreshed); + paint(child, (req_refresh_children ? paint_operation::try_refresh : paint_operation::none), req_refresh_children); continue; } @@ -289,7 +289,7 @@ namespace nana bool have_child_refreshed = false; if (category::flags::lite_widget != child->other.category) { - if (is_child_refreshed && (false == child->flags.refreshing)) + if (req_refresh_children && (false == child->flags.refreshing)) { have_child_refreshed = true; child->flags.refreshing = true; @@ -300,13 +300,13 @@ namespace nana graph.bitblt(nana::rectangle(rect.x - graph_rpos.x, rect.y - graph_rpos.y, rect.width, rect.height), child->drawer.graphics, nana::point(rect.x - child->pos_root.x, rect.y - child->pos_root.y)); } - _m_paste_children(child, is_child_refreshed, have_child_refreshed, rect, graph, graph_rpos); + _m_paste_children(child, req_refresh_children, have_child_refreshed, rect, graph, graph_rpos); } } else { - //If have_refreshed, the glass should be notified. - _m_paint_glass_window(child, false, is_child_refreshed, have_refreshed, false); + //Update the glass window's background if the parent have_refreshed. + _m_paint_glass_window(child, have_refreshed, req_refresh_children, true, false); } } } @@ -353,16 +353,14 @@ namespace nana } if (notify_other) - _m_notify_glasses(wd, vr); + _m_notify_glasses(wd); } } - //_m_notify_glasses - //@brief: Notify the glass windows that are overlapped with the specified vis_rect - void window_layout::_m_notify_glasses(core_window_t* const sigwd, const nana::rectangle& /*r_visual*/) + /// Notify the glass windows that are overlapped with the specified visual rectangle. + /// If a child window of sigwd is a glass window, it doesn't to be notified. + void window_layout::_m_notify_glasses(core_window_t* const sigwd) { - typedef category::flags cat_flags; - nana::rectangle r_of_sigwd(sigwd->pos_root, sigwd->dimension); for (auto wd : data_sect.effects_bground_windows) { @@ -377,6 +375,8 @@ namespace nana } else if (sigwd != wd->parent) { + using cat_flags = category::flags; + if (wd->parent && (cat_flags::lite_widget == wd->parent->other.category)) { //Test if sigwd is an ancestor of the glass window, and there are lite widgets diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 5cfe1d10..984988ee 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -759,6 +759,10 @@ namespace detail arg.window_handle = reinterpret_cast(wd); arg.x = x; arg.y = y; + + if (wd->effect.bground) + wd->other.upd_state = basic_window::update_state::request_refresh; + brock.emit(event_code::move, wd, arg, true, brock.get_thread_context()); return true; } @@ -803,6 +807,9 @@ namespace detail _m_move_core(wd, delta); moved = true; + if ((!size_changed) && wd->effect.bground) + wd->other.upd_state = basic_window::update_state::request_refresh; + arg_move arg; arg.window_handle = reinterpret_cast(wd); arg.x = r.x; @@ -989,26 +996,28 @@ namespace detail if (wd->displayed()) { + using paint_operation = window_layer::paint_operation; + if(forced || (false == wd->belong_to_lazy())) { if (!wd->flags.refreshing) { - window_layer::paint(wd, redraw, false); + window_layer::paint(wd, (redraw ? paint_operation::try_refresh : paint_operation::none), false); this->map(wd, forced, update_area); return true; } else if (forced) { - window_layer::paint(wd, false, false); + window_layer::paint(wd, paint_operation::none, false); this->map(wd, true, update_area); return true; } } else if (redraw) - window_layer::paint(wd, true, false); + window_layer::paint(wd, paint_operation::try_refresh, false); if (wd->other.upd_state == core_window_t::update_state::lazy) - wd->other.upd_state = core_window_t::update_state::refresh; + wd->other.upd_state = core_window_t::update_state::refreshed; } return true; } @@ -1020,28 +1029,28 @@ namespace detail //It's not worthy to redraw if visible is false if (impl_->wd_register.available(wd) && wd->displayed()) - window_layer::paint(wd, true, true); + window_layer::paint(wd, window_layer::paint_operation::try_refresh, true); } //do_lazy_refresh //@brief: defined a behavior of flush the screen - //@return: it returns true if the wnd is available - bool window_manager::do_lazy_refresh(core_window_t* wd, bool force_copy_to_screen, bool refresh_tree) + void window_manager::do_lazy_refresh(core_window_t* wd, bool force_copy_to_screen, bool refresh_tree) { //Thread-Safe Required! std::lock_guard lock(mutex_); if (false == impl_->wd_register.available(wd)) - return false; + return; //It's not worthy to redraw if visible is false if(wd->visible && (!wd->is_draw_through())) { + using paint_operation = window_layer::paint_operation; if (wd->visible_parents()) { - if ((wd->other.upd_state == core_window_t::update_state::refresh) || force_copy_to_screen) + if ((wd->other.upd_state == core_window_t::update_state::refreshed) || (wd->other.upd_state == core_window_t::update_state::request_refresh) || force_copy_to_screen) { - window_layer::paint(wd, false, refresh_tree); + window_layer::paint(wd, (wd->other.upd_state == core_window_t::update_state::request_refresh ? paint_operation::try_refresh : paint_operation::have_refreshed), refresh_tree); this->map(wd, force_copy_to_screen); } else if (effects::edge_nimbus::none != wd->effect.edge_nimbus) @@ -1053,10 +1062,10 @@ namespace detail } } else - window_layer::paint(wd, true, refresh_tree); //only refreshing if it has an invisible parent + window_layer::paint(wd, paint_operation::try_refresh, refresh_tree); //only refreshing if it has an invisible parent } wd->other.upd_state = core_window_t::update_state::none; - return true; + return; } //get_graphics From 3d2dbf60af976798beed570b200d3a4d802ff485 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 14 Oct 2016 21:54:09 +0800 Subject: [PATCH 06/16] fix bug that mousedown/click would not triggerred --- source/gui/detail/bedrock_posix.cpp | 33 ++++++++++++++------------- source/gui/detail/bedrock_windows.cpp | 8 ++++++- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 6e6602d9..537e602e 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -567,9 +567,12 @@ namespace detail if(xevent.xbutton.button == Button4 || xevent.xbutton.button == Button5) break; - + msgwnd = wd_manager.find_window(native_window, xevent.xbutton.x, xevent.xbutton.y); - if(nullptr == msgwnd) break; + + pressed_wd = nullptr; + if(nullptr == msgwnd) + break; if ((msgwnd == msgwnd->root_widget->other.attribute.root->menubar) && brock.get_menu(msgwnd->root, true)) brock.erase_menu(true); @@ -578,7 +581,9 @@ namespace detail if(msgwnd->flags.enabled) { - bool dbl_click = (last_mouse_down_window == msgwnd) && (xevent.xbutton.time - last_mouse_down_time <= 400); + pressed_wd = msgwnd; + + const bool dbl_click = (last_mouse_down_window == msgwnd) && (xevent.xbutton.time - last_mouse_down_time <= 400); last_mouse_down_time = xevent.xbutton.time; last_mouse_down_window = msgwnd; @@ -597,28 +602,24 @@ namespace detail auto retain = msgwnd->annex.events_ptr; context.event_window = msgwnd; - pressed_wd = nullptr; - msgwnd->set_action(mouse_action::pressed); arg_mouse arg; assign_arg(arg, msgwnd, ButtonPress, xevent); arg.evt_code = dbl_click ? event_code::dbl_click : event_code::mouse_down; - if(brock.emit(arg.evt_code, msgwnd, arg, true, &context)) + if (brock.emit(arg.evt_code, msgwnd, arg, true, &context)) { - if (wd_manager.available(msgwnd)) + //If a root window is created during the mouse_down event, Nana.GUI will ignore the mouse_up event. + if (msgwnd->root != native_interface::get_focus_window()) { - pressed_wd = msgwnd; - //If a root window is created during the mouse_down event, Nana.GUI will ignore the mouse_up event. - if (msgwnd->root != native_interface::get_focus_window()) - { - //call the drawer mouse up event for restoring the surface graphics - msgwnd->set_action(mouse_action::normal); + //call the drawer mouse up event for restoring the surface graphics + msgwnd->set_action(mouse_action::normal); - draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); - wd_manager.do_lazy_refresh(msgwnd, false); - } + draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); + wd_manager.do_lazy_refresh(msgwnd, false); } } + else + pressed_wd = nullptr; } break; case ButtonRelease: diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index a790a0c2..594c949f 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -919,7 +919,13 @@ namespace detail break; msgwnd = wd_manager.find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); - if ((nullptr == msgwnd) || (pressed_wd && (msgwnd != pressed_wd))) + + //Don't take care about whether msgwnd is equal to the pressed_wd. + // + //pressed_wd will remains when opens a no-actived window in an mouse_down event(like combox popups the drop-list). + //After the no-actived window is closed, the window doesn't respond to the mouse click other than pressed_wd. + pressed_wd = nullptr; + if (nullptr == msgwnd) break; //if event on the menubar, just remove the menu if it is not associating with the menubar From 1332b3e6d89c8ae71dae041eca10f9f8394e4cd6 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 14 Oct 2016 23:00:46 +0800 Subject: [PATCH 07/16] fix a compiling error --- source/gui/detail/bedrock_posix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 537e602e..3282a32e 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -1204,7 +1204,7 @@ namespace detail { thread_context* thrd = get_thread_context(0); if(thrd && thrd->event_window) - thrd->event_window->other.upd_state = core_window_t::update_state::refresh; + thrd->event_window->other.upd_state = core_window_t::update_state::refreshed; } //Dynamically set a cursor for a window From 01ff1c11c12bdc40c77ac6e9752c76a952138f51 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 15 Oct 2016 07:05:43 +0800 Subject: [PATCH 08/16] fix bug that some events of textbox would not be triggered --- source/gui/widgets/textbox.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 67d6436c..c51fb4d4 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -69,10 +69,10 @@ namespace drawerbase { auto scheme = API::dev::get_scheme(wdg); editor_ = new text_editor(wd, graph, dynamic_cast<::nana::widgets::skeletons::text_editor_scheme*>(scheme)); - editor_->textbase().set_event_agent(evt_agent_.get()); - editor_->set_event(evt_agent_.get()); evt_agent_.reset(new event_agent(static_cast<::nana::textbox&>(wdg), editor_->text_position())); + editor_->textbase().set_event_agent(evt_agent_.get()); + editor_->set_event(evt_agent_.get()); _m_text_area(graph.width(), graph.height()); From e7181a700783867da86553c3e103d858f9eb99e6 Mon Sep 17 00:00:00 2001 From: Michal Fojtak Date: Mon, 17 Oct 2016 15:27:38 +0200 Subject: [PATCH 09/16] vs"15" fix --- include/nana/c++defines.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index bd39a822..ecc5079c 100644 --- a/include/nana/c++defines.hpp +++ b/include/nana/c++defines.hpp @@ -102,7 +102,7 @@ #define _CRT_SECURE_NO_DEPRECATE #pragma warning(disable : 4996) - #if (_MSC_VER == 1900) + #if (_MSC_VER >= 1900) // google: break any code that tries to use codecvt or codecvt. // google: It appears the C++ libs haven't been compiled with native char16_t/char32_t support. // google: Those definitions are for codecvt::id, codecvt::id and codecvt::id respectively. From 9ed5e81ca2846dbe0ffbc3a38b06ff3eed23f300 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 19 Oct 2016 02:58:49 +0800 Subject: [PATCH 10/16] fix the revertable lock of window_manager --- include/nana/threads/pool.hpp | 6 ++++++ source/gui/detail/bedrock_posix.cpp | 9 +++++--- source/gui/detail/window_manager.cpp | 12 ++++++++--- source/gui/programming_interface.cpp | 31 +++++++++++++--------------- source/gui/wvl.cpp | 1 + source/threads/pool.cpp | 17 +++++++++++++++ 6 files changed, 53 insertions(+), 23 deletions(-) diff --git a/include/nana/threads/pool.hpp b/include/nana/threads/pool.hpp index 16edac1f..38d9952e 100644 --- a/include/nana/threads/pool.hpp +++ b/include/nana/threads/pool.hpp @@ -54,11 +54,17 @@ namespace threads struct task_signal; class impl; + + pool(const pool&) = delete; + pool& operator=(const pool&) = delete; public: pool(); ///< Creates a group of threads. + pool(pool&&); pool(std::size_t thread_number); ///< Creates a number of threads specifed by thread_number. ~pool(); ///< waits for the all running tasks till they are finished and skips all the queued tasks. + pool& operator=(pool&&); + template void push(const Function& f) { diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 3282a32e..4f74f5a9 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -1158,7 +1158,9 @@ namespace detail } ++(context->event_pump_ref_count); - wd_manager().internal_lock().revert(); + + auto & lock = wd_manager().internal_lock(); + lock.revert(); native_window_type owner_native = 0; core_window_t * owner = 0; @@ -1183,8 +1185,9 @@ namespace detail owner->flags.enabled = true; native_interface::enable_window(owner_native, true); } - - wd_manager().internal_lock().forward(); + + lock.forward(); + if(0 == --(context->event_pump_ref_count)) { if(0 == modal_window || 0 == context->window_count) diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 984988ee..e301ca5e 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -335,7 +335,7 @@ namespace detail void window_manager::revertible_mutex::revert() { - if(impl_->thread.ref && (impl_->thread.tid == nana::system::this_thread_id())) + if(impl_->thread.tid == nana::system::this_thread_id()) { std::size_t cnt = impl_->thread.ref; @@ -346,24 +346,30 @@ namespace detail for (std::size_t i = 0; i < cnt; ++i) impl_->mutex.unlock(); } + else + throw std::runtime_error("The revert is not allowed"); } void window_manager::revertible_mutex::forward() { impl_->mutex.lock(); + if(impl_->invoke_stack.size()) { auto thr = impl_->invoke_stack.back(); + + impl_->invoke_stack.pop_back(); + if(thr.tid == nana::system::this_thread_id()) { - impl_->invoke_stack.pop_back(); for (std::size_t i = 0; i < thr.ref; ++i) impl_->mutex.lock(); impl_->thread = thr; } else - throw std::runtime_error("Nana.GUI: The forward is not matched."); + throw std::runtime_error("The forward is not matched. Please report this issue"); } + impl_->mutex.unlock(); } //end class revertible_mutex diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 94446c8b..486e6fae 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -992,33 +992,30 @@ namespace API void modal_window(window wd) { + auto const iwd = reinterpret_cast(wd); + internal_scope_guard isg; + + if (!restrict::wd_manager().available(iwd)) + return; + + if ((iwd->other.category == category::flags::root) && (iwd->flags.modal == false)) { - auto const iwd = reinterpret_cast(wd); - internal_scope_guard isg; - - if (!restrict::wd_manager().available(iwd)) - return; - - if ((iwd->other.category == category::flags::root) && (iwd->flags.modal == false)) - { - iwd->flags.modal = true; + iwd->flags.modal = true; #if defined(NANA_X11) - interface_type::set_modal(iwd->root); + interface_type::set_modal(iwd->root); #endif - restrict::wd_manager().show(iwd, true); - } - else - return; + restrict::wd_manager().show(iwd, true); } + else + return; - //modal has to guarantee that does not lock the mutex of window_manager before invokeing the pump_event, - //otherwise, the modal will prevent the other thread access the window. restrict::bedrock.pump_event(wd, true); } void wait_for(window wd) { - if (wd) + internal_scope_guard lock; + if (restrict::wd_manager().available(reinterpret_cast(wd))) restrict::bedrock.pump_event(wd, false); } diff --git a/source/gui/wvl.cpp b/source/gui/wvl.cpp index 33f027f3..27e7e5ea 100644 --- a/source/gui/wvl.cpp +++ b/source/gui/wvl.cpp @@ -46,6 +46,7 @@ namespace nana void pump() { + internal_scope_guard lock; detail::bedrock::instance().pump_event(nullptr, false); } diff --git a/source/threads/pool.cpp b/source/threads/pool.cpp index f1237887..0e2c6c29 100644 --- a/source/threads/pool.cpp +++ b/source/threads/pool.cpp @@ -356,11 +356,28 @@ namespace threads { } + pool::pool(pool&& other) + : pool() + { + std::swap(impl_, other.impl_); + } + pool::pool(std::size_t thread_number) : impl_(new impl(thread_number)) { } + pool& pool::operator=(pool&& other) + { + if(this != &other) + { + delete impl_; + impl_ = other.impl_; + other.impl_ = new impl(4); + } + return *this; + } + pool::~pool() { delete impl_; From e7ce506f5a1d95ed63bb3565c456716618887a6a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 19 Oct 2016 04:01:40 +0800 Subject: [PATCH 11/16] fix an exception of msg_dispatcher --- source/detail/x11/msg_dispatcher.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/detail/x11/msg_dispatcher.hpp b/source/detail/x11/msg_dispatcher.hpp index 2f1688e0..77f43a18 100644 --- a/source/detail/x11/msg_dispatcher.hpp +++ b/source/detail/x11/msg_dispatcher.hpp @@ -58,6 +58,15 @@ namespace detail proc_.filter_proc = 0; } + ~msg_dispatcher() + { + if(thrd_ && thrd_->joinable()) + { + is_work_ = false; + thrd_->join(); + } + } + void set(timer_proc_type timer_proc, event_proc_type event_proc, event_filter_type filter) { proc_.timer_proc = timer_proc; From 021ee1edb457be8dd14de8c163e82083e6624219 Mon Sep 17 00:00:00 2001 From: PeterAddy960 Date: Fri, 28 Oct 2016 00:05:56 -0500 Subject: [PATCH 12/16] Added a clear undo method to the textbox to erase all previous undo actions. Added an overload to the selected method that give back the bounds of the selection. --- include/nana/gui/widgets/skeletons/text_editor.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 22e099e4..a13c9dcf 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -205,6 +205,8 @@ namespace nana{ namespace widgets void undo_max_steps(std::size_t); std::size_t undo_max_steps() const; + void clear_undo(); + ext_renderer_tag& ext_renderer() const; unsigned line_height() const; @@ -226,6 +228,7 @@ namespace nana{ namespace widgets void show_caret(bool isshow); bool selected() const; + bool selected(nana::upoint &a,nana::upoint &b) const; bool select(bool); /// Sets the end position of a selected string. void set_end_caret(); From ea1edb5a4a12fe2cb3180ce85451350e81ab22b4 Mon Sep 17 00:00:00 2001 From: PeterAddy960 Date: Fri, 28 Oct 2016 00:07:33 -0500 Subject: [PATCH 13/16] Added a clear undo method. Added an overload to selected to get the selection bounds. Added an overload to the getline method to allow an offset from the beginning of the line. --- include/nana/gui/widgets/textbox.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 12008c37..c74fac54 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -162,6 +162,9 @@ namespace nana /// Read the text from a specified line. It returns true for success. bool getline(std::size_t pos, std::string&) const; + /// Read the text from a specified line with a set offset. It returns true for success. + bool getline(std::size_t line_index,std::size_t offset,std::string& text) const; + /// Gets the caret position /// Returns true if the caret is in the area of display, false otherwise. bool caret_pos(point& pos, bool text_coordinate) const; @@ -193,6 +196,7 @@ namespace nana /// Returns true if some text is selected. bool selected() const; + bool selected(nana::upoint &a,nana::upoint &b) const; /// Selects/unselects all text. void select(bool); @@ -206,6 +210,8 @@ namespace nana textbox& from(int); textbox& from(double); + void clear_undo(); + void set_highlight(const std::string& name, const ::nana::color& fgcolor, const ::nana::color& bgcolor); void erase_highlight(const std::string& name); void set_keywords(const std::string& name, bool case_sensitive, bool whole_word_match, std::initializer_list kw_list); From 78c3527f10c5f5ee90272192bf505d7344a491b4 Mon Sep 17 00:00:00 2001 From: PeterAddy960 Date: Fri, 28 Oct 2016 00:10:11 -0500 Subject: [PATCH 14/16] Modified the implementation so that nana::keyboard::os_del is sent throght the set_accept function, so that os_del can be blocked. Added overload to selected to get the selection bounds. --- source/gui/widgets/skeletons/text_editor.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index a030471a..5574f9a2 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1602,6 +1602,11 @@ namespace nana{ namespace widgets return undo_.max_steps(); } + void text_editor::clear_undo() + { + undo_.clear(); + } + text_editor::ext_renderer_tag& text_editor::ext_renderer() const { return ext_renderer_; From 8e84383a7b9addb965177944f67ffce246f22f0d Mon Sep 17 00:00:00 2001 From: PeterAddy960 Date: Fri, 28 Oct 2016 00:17:59 -0500 Subject: [PATCH 15/16] Added an overload to getline to allow an offset from the beginning of the line. Added a clear undo method. Added an overload to selected to get the selected bounds. --- source/gui/widgets/skeletons/text_editor.cpp | 10 ++++++- source/gui/widgets/textbox.cpp | 31 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 5574f9a2..d8b4cd56 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1427,7 +1427,8 @@ namespace nana{ namespace widgets _handle_move_key(arg); break; case keyboard::os_del: - if (this->attr().editable) + // send delete to set_accept function + if (this->attr().editable && (!attributes_.pred_acceptive || attributes_.pred_acceptive(key))) del(); break; default: @@ -1916,6 +1917,13 @@ namespace nana{ namespace widgets return (select_.a != select_.b); } + bool text_editor::selected(nana::upoint &a,nana::upoint &b) const + { + a = select_.a; + b = select_.b; + return selected(); + } + void text_editor::set_end_caret() { bool new_sel_end = (select_.b != points_.caret); diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 76aed3ac..c6c5575c 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -318,6 +318,25 @@ namespace drawerbase { return false; } + bool textbox::getline(std::size_t line_index,std::size_t start_point,std::string& text) const + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if(editor) + { + std::wstring line_text; + if(editor->getline(line_index,line_text)) + { + if(line_text.length() >= start_point) + { + text = to_utf8(line_text.substr(start_point)); + return true; + } + } + } + return false; + } + /// Gets the caret position bool textbox::caret_pos(point& pos, bool text_coordinate) const { @@ -451,6 +470,13 @@ namespace drawerbase { return (editor ? editor->selected() : false); } + bool textbox::selected(nana::upoint &a,nana::upoint &b) const + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + return (editor ? editor->selected(a,b) : false); + } + void textbox::select(bool yes) { internal_scope_guard lock; @@ -517,6 +543,11 @@ namespace drawerbase { return *this; } + void textbox::clear_undo() + { + get_drawer_trigger().editor()->clear_undo(); + } + void textbox::set_highlight(const std::string& name, const ::nana::color& fgcolor, const ::nana::color& bgcolor) { internal_scope_guard lock; From 445f0ed6de42a371f217928c0492b0c6eab31e10 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 31 Oct 2016 04:33:42 +0800 Subject: [PATCH 16/16] fix issue of menu item_proxy --- include/nana/gui/widgets/menu.hpp | 22 +-- source/gui/widgets/menu.cpp | 308 +++++++++++++++--------------- source/gui/widgets/menubar.cpp | 2 +- 3 files changed, 157 insertions(+), 175 deletions(-) diff --git a/include/nana/gui/widgets/menu.hpp b/include/nana/gui/widgets/menu.hpp index 02372079..f364f15c 100644 --- a/include/nana/gui/widgets/menu.hpp +++ b/include/nana/gui/widgets/menu.hpp @@ -13,10 +13,7 @@ #ifndef NANA_GUI_WIDGETS_MENU_HPP #define NANA_GUI_WIDGETS_MENU_HPP #include "widget.hpp" -#include -#include #include - #include namespace nana @@ -71,25 +68,12 @@ namespace nana menu_type *sub_menu{nullptr}; std::string text; - event_fn_t functor; + event_fn_t event_handler; checks style{checks::none}; paint::image image; mutable wchar_t hotkey{0}; }; - struct menu_type - { - typedef std::vector item_container; - typedef item_container::iterator iterator; - typedef item_container::const_iterator const_iterator; - - std::vector owner; - std::vector items; - unsigned max_pixels; - unsigned item_pixels; - nana::point gaps; - }; - class renderer_interface { public: @@ -156,7 +140,7 @@ namespace nana void popup(window owner, int x, int y); ///< Popup the menu at the owner window. void popup_await(window owner, int x, int y); void answerer(std::size_t index, const event_fn_t&); ///< Modify answerer of the specified item. - void destroy_answer(const std::function&); ///< Sets an answerer for the callback while the menu window is closing. + void destroy_answer(std::function); ///< Sets an answerer for the callback while the menu window is closing. void gaps(const nana::point&); ///< Sets the gap between a menu and its sub menus.(\See Note4) void goto_next(bool forward); ///< Moves the focus to the next or previous item. bool goto_submen();///< Popup the submenu of the current item if it has a sub menu. Returns true if succeeds. @@ -175,7 +159,7 @@ namespace nana const pat::cloneable& renderer() const; private: - void _m_popup(window, int x, int y, bool called_by_menubar); + void _m_popup(window, const point& position, bool called_by_menubar); private: implement * impl_; }; diff --git a/source/gui/widgets/menu.cpp b/source/gui/widgets/menu.cpp index 05c428ce..ab812c77 100644 --- a/source/gui/widgets/menu.cpp +++ b/source/gui/widgets/menu.cpp @@ -1,7 +1,7 @@ /* * A Menu implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2009-2015 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2009-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -13,12 +13,15 @@ */ #include +#include + #include #include #include #include #include //introduces tolower #include +#include namespace nana { @@ -26,6 +29,20 @@ namespace nana { namespace menu { + + struct menu_type + { + using item_type = menu_item_type; + using item_container = std::vector>; + using iterator = item_container::iterator; + + std::vector owner; + item_container items; + unsigned max_pixels; + unsigned item_pixels; + nana::point gaps; + }; + //A helper function to check the style parameter inline bool good_checks(checks s) { @@ -82,8 +99,8 @@ namespace nana flags.checked = false; } - menu_item_type::menu_item_type(std::string text, const event_fn_t& f) - : text(std::move(text)), functor(f) + menu_item_type::menu_item_type(std::string text, const event_fn_t& fn) + : text(std::move(text)), event_handler(fn) { flags.enabled = true; flags.splitter = false; @@ -152,10 +169,10 @@ namespace nana //Stretchs menu icon only when it doesn't fit, center it otherwise. //Contributed by kmribti(pr#102) - nana::point ipos = pos; - ipos.x += (image_px - img.size().width ) / 2; - ipos.y += (image_px - img.size().height) / 2; - img.paste(graph, ipos); + img.paste(graph, { + pos.x + static_cast(image_px - img.size().width) / 2, + pos.y + static_cast(image_px - img.size().height) / 2 + }); } void item_text(graph_reference graph, const nana::point& pos, const std::string& text, unsigned text_pixels, const attr& at) @@ -182,11 +199,9 @@ namespace nana : noncopyable { public: - typedef menu_item_type item_type; - - typedef menu_type::item_container::value_type::event_fn_t event_fn_t; - typedef menu_type::item_container::iterator iterator; - typedef menu_type::item_container::const_iterator const_iterator; + using item_type = menu_item_type; + using event_fn_t = item_type::event_fn_t; + using iterator = menu_type::item_container::iterator; menu_builder() { @@ -205,40 +220,42 @@ namespace nana if(good_checks(s)) { if(root_.items.size() > index) - root_.items[index].style = s; + root_.items[index]->style = s; } } - void checked(std::size_t index, bool check) + void checked(std::size_t pos, bool check) { - if (root_.items.size() <= index) + if (root_.items.size() <= pos) return; - item_type & m = root_.items[index]; + item_type & m = *(root_.items[pos]); + if(check && (checks::option == m.style)) { - if(index) + //find a splitter in front of pos + if (pos > 0) { - 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); + if (root_.items[--pos]->flags.splitter) + { + ++pos; + break; + } + }while(pos > 0); } - for(std::size_t i = index + 1; i < root_.items.size(); ++i) + while (pos < root_.items.size()) { - item_type & el = root_.items[i]; + item_type & el = *(root_.items[pos++]); if(el.flags.splitter) break; if(checks::option == el.style) el.flags.checked = false; } } + m.flags.checked = check; } @@ -247,25 +264,12 @@ namespace nana return root_; } - const menu_type& data() const - { - return root_; - } - - void insert(std::size_t pos, std::string&& text, const event_fn_t& fn) - { - if(pos < root_.items.size()) - root_.items.emplace(root_.items.begin() + pos, std::move(text), std::ref(fn)); - else - root_.items.emplace_back(std::move(text), std::ref(fn)); - } - bool set_sub_menu(std::size_t pos, menu_type &sub) { if(root_.items.size() > pos) { - menu_item_type & item = root_.items[pos]; - if(item.sub_menu == nullptr) + auto & item = *(root_.items[pos]); + if(!item.sub_menu) { item.sub_menu = ⊂ sub.owner.emplace_back(&root_); @@ -280,17 +284,17 @@ namespace nana for(auto i : root_.owner) for(auto & m : i->items) { - if(m.sub_menu == &root_) - m.sub_menu = nullptr; + if(m->sub_menu == &root_) + m->sub_menu = nullptr; } for(auto & m : root_.items) { - if(m.sub_menu) - for(auto i = m.sub_menu->owner.begin(); i != m.sub_menu->owner.end();) + if(m->sub_menu) + for(auto i = m->sub_menu->owner.begin(); i != m->sub_menu->owner.end();) { if((*i) == &root_) - i = m.sub_menu->owner.erase(i); + i = m->sub_menu->owner.erase(i); else ++i; } @@ -317,8 +321,6 @@ namespace nana public: using item_proxy = menu_item_type::item_proxy; - renderer_interface * renderer; - menu_drawer() { state_.active = npos; @@ -328,9 +330,16 @@ namespace nana detail_.border.x = detail_.border.y = 2; } - void close_menu_tree(std::function && fn) + void set_run(menu_builder& mbuilder, menu_type& menu, std::function && menu_tree_destroyer) { - fn_close_tree_ = std::move(fn); + mbuilder_ = &mbuilder; + menu_ = &menu; + fn_close_tree_ = std::move(menu_tree_destroyer); + } + + menu_builder& mbuilder() + { + return *mbuilder_; } void attached(widget_reference widget, graph_reference graph) @@ -363,10 +372,12 @@ namespace nana void refresh(graph_reference graph) { - if (nullptr == menu_) return; + if (!(mbuilder_ && menu_)) + return; _m_adjust_window_size(); + auto renderer = mbuilder_->renderer().get(); renderer->background(graph, *widget_); const unsigned item_h_px = _m_item_height(); @@ -375,12 +386,13 @@ namespace nana unsigned strpixels = item_r.width - 60; - int text_top_off = (item_h_px - graph.text_extent_size(L"jh({[").height) / 2; + int text_top_off = static_cast(item_h_px - graph.text_extent_size(L"jh({[").height) / 2; std::size_t pos = 0; for (auto & m : menu_->items) { - if (m.flags.splitter) + auto item_ptr = m.get(); + if (item_ptr->flags.splitter) { graph_->line({ item_r.x + 40, item_r.y }, { static_cast(graph.width()) - 1, item_r.y }, colors::gray_border); item_r.y += 2; @@ -388,24 +400,24 @@ namespace nana continue; } - renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, m); + renderer_interface::attr attr = _m_make_renderer_attr(pos == state_.active, *item_ptr); //Draw item background renderer->item(*graph_, item_r, attr); //Draw text, the text is transformed from orignal for hotkey character wchar_t hotkey; std::string::size_type hotkey_pos; - auto text = API::transform_shortkey_text(m.text, hotkey, &hotkey_pos); + auto text = API::transform_shortkey_text(item_ptr->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); + if (item_ptr->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, item_ptr->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) + item_ptr->hotkey = hotkey; + if (item_ptr->flags.enabled) { auto off_px = (hotkey_pos ? graph.text_extent_size(text.c_str(), hotkey_pos).width : 0); auto hotkey_px = graph.text_extent_size(text.c_str() + hotkey_pos, 1).width; @@ -420,7 +432,7 @@ namespace nana } } - if (m.sub_menu) + if (item_ptr->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; @@ -437,11 +449,14 @@ namespace nana bool goto_next(bool forword) { state_.nullify_mouse = true; - if (menu_->items.empty()) + + auto & items = menu_->items; + + if (items.empty()) return false; auto pos = state_.active; - const auto lastpos = menu_->items.size() - 1; + const auto lastpos = items.size() - 1; bool end = false; while(true) @@ -476,7 +491,8 @@ namespace nana --pos; } - if(! menu_->items.at(pos).flags.splitter && menu_->items.at(pos).flags.enabled) + auto item_ptr = items.at(pos).get(); + if (!item_ptr->flags.splitter && item_ptr->flags.enabled) break; } @@ -496,13 +512,14 @@ namespace nana { if (!state_.nullify_mouse) { + auto & items = menu_->items; 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) && items.at(state_.active)->sub_menu && state_.sub_window) return false; - state_.active = (index != npos && menu_->items.at(index).flags.splitter) ? npos : index; + state_.active = (index != npos && items.at(index)->flags.splitter) ? npos : index; state_.active_timestamp = nana::system::timestamp(); return true; } @@ -511,11 +528,6 @@ namespace nana return false; } - void data(menu_type & menu) - { - menu_ = & menu; - } - menu_type* data() const { return menu_; @@ -531,19 +543,20 @@ namespace nana if (npos == state_.active) return nullptr; - auto sub = menu_->items.at(state_.active).sub_menu; + auto & items = menu_->items; + auto sub = items.at(state_.active)->sub_menu; if (sub) { pos.x = static_cast(graph_->width()) - 2; pos.y = 2; auto index = state_.active; - for (auto & m : menu_->items) + for (auto & m : items) { if (0 == index--) break; - if (m.flags.splitter) + if (m->flags.splitter) { pos.y += 2; continue; @@ -565,15 +578,16 @@ namespace nana std::size_t index = 0; for(auto & m : menu_->items) { - if (std::tolower(m.hotkey) != key) + auto item_ptr = m.get(); + if (std::tolower(m->hotkey) != key) { ++index; continue; } - if(!m.flags.splitter) + if (!item_ptr->flags.splitter) { - if(m.sub_menu) + if (item_ptr->sub_menu) { state_.active = index; state_.active_timestamp = nana::system::timestamp(); @@ -581,13 +595,13 @@ namespace nana API::refresh_window(*widget_); return 2; } - else if(m.flags.enabled) + else if (item_ptr->flags.enabled) { fn_close_tree_(); - if (m.functor) + if (item_ptr->event_handler) { - item_proxy ip(index, m); - m.functor.operator()(ip); + item_proxy ip(index, *item_ptr); + item_ptr->event_handler.operator()(ip); } return 1; } @@ -617,9 +631,9 @@ namespace nana int pos = detail_.border.y; std::size_t index = 0; - for(auto & m : menu_->items) + for (auto & m : menu_->items) { - unsigned h = (m.flags.splitter ? 1 : _m_item_height()); + unsigned h = (m->flags.splitter ? 1 : _m_item_height()); if(pos <= y && y < static_cast(pos + h)) return index; else if(y < pos) @@ -640,13 +654,13 @@ namespace nana { nana::size size; - if(menu_->items.size()) + if (menu_->items.size()) { - for(auto & m : menu_->items) + for (auto & m : menu_->items) { - if(false == m.flags.splitter) + if(false == m->flags.splitter) { - nana::size item_size = graph_->text_extent_size(m.text); + nana::size item_size = graph_->text_extent_size(m->text); if(size.width < item_size.width) size.width = item_size.width; } @@ -658,7 +672,7 @@ namespace nana size.height = static_cast(menu_->items.size() - size.height) * _m_item_height() + size.height + static_cast(menu_->items.size() - 1); } - if(size.width > menu_->max_pixels) + if (size.width > menu_->max_pixels) size.width = menu_->max_pixels; return size; @@ -687,14 +701,15 @@ namespace nana pos.y = scr_area.bottom() - static_cast(size.height); if(pos.y < scr_area.y) pos.y = scr_area.y; - auto owner = API::get_owner_window(*widget_); - API::calc_window_point(owner, pos); + API::calc_window_point(API::get_owner_window(*widget_), pos); widget_->move(pos.x, pos.y); } private: widget *widget_{nullptr}; paint::graphics *graph_{nullptr}; - menu_type *menu_{nullptr}; + + menu_builder* mbuilder_{ nullptr }; + menu_type* menu_{ nullptr }; std::function fn_close_tree_; @@ -722,7 +737,7 @@ namespace nana using item_type = menu_builder::item_type; - menu_window(window wd, bool is_wd_parent_menu, const point& pos, renderer_interface * rdptr) + menu_window(window wd, bool is_wd_parent_menu, const point& pos, menu_builder& mbuilder, menu_type& menu_data) //add a is_wd_parent_menu to determine whether the menu wants the focus. //if a submenu gets the focus, the program may cause a crash error when the submenu is being destroyed : base_type(wd, false, rectangle(pos, nana::size(2, 2)), appear::bald()), @@ -730,8 +745,8 @@ namespace nana event_focus_{ nullptr } { caption("nana menu window"); - get_drawer_trigger().close_menu_tree([this]{ this->_m_close_all(); }); - get_drawer_trigger().renderer = rdptr; + get_drawer_trigger().set_run(mbuilder, menu_data, [this]{ this->_m_close_all(); }); + state_.owner_menubar = state_.self_submenu = false; state_.auto_popup_submenu = true; @@ -739,7 +754,7 @@ namespace nana submenu_.object = nullptr; state_.mouse_pos = API::cursor_position(); - events().mouse_move.connect_unignorable([this]{ + events().mouse_move.connect_unignorable([this](const arg_mouse&){ nana::point pos = API::cursor_position(); if (pos != state_.mouse_pos) { @@ -753,10 +768,8 @@ namespace nana }); } - void popup(menu_type& menu, bool owner_menubar) + void popup(bool owner_menubar) { - get_drawer_trigger().data(menu); - if (!want_focus_) { API::activate_window(this->parent()); @@ -770,7 +783,7 @@ namespace nana } auto & events = this->events(); - events.destroy.connect_unignorable([this]{ + events.destroy.connect_unignorable([this](const arg_destroy&){ _m_destroy(); }); @@ -880,7 +893,7 @@ namespace nana if ((npos == active) || !menu) return; - menu_item_type & item = menu->items.at(active); + menu_item_type & item = *(menu->items.at(active)); if ((!item.flags.enabled) || item.flags.splitter || item.sub_menu) return; @@ -891,35 +904,18 @@ namespace nana } else if (checks::option == item.style) { - //Forward Looks for a splitter - auto pos = active; - while (pos) - { - if (menu->items.at(--pos).flags.splitter) - break; - } - - for (; pos < menu->items.size(); ++pos) - { - menu_item_type & im = menu->items.at(pos); - if (im.flags.splitter) break; - - if ((checks::option == im.style) && im.flags.checked) - im.flags.checked = false; - } - - item.flags.checked = true; + get_drawer_trigger().mbuilder().checked(active, true); } this->_m_close_all(); //means deleting this; - //The deleting operation has moved here, because item.functor.operator()(ip) + //The deleting operation has moved here, because item.event_handler.operator()(ip) //may create a window, which make a killing focus for menu window, if so the close_all - //operation preformences after item.functor.operator()(ip), that would be deleting this object twice! + //operation preformences after item.event_handler.operator()(ip), that would be deleting this object twice! - if (item.functor) + if (item.event_handler) { item_type::item_proxy ip(active, item); - item.functor.operator()(ip); + item.event_handler.operator()(ip); } } private: @@ -1034,7 +1030,7 @@ namespace nana menu_ptr->gaps = drawer.data()->gaps; pos += menu_ptr->gaps; - menu_window & mwnd = form_loader()(handle(), true, pos, drawer.renderer); + menu_window & mwnd = form_loader()(handle(), true, pos, drawer.mbuilder(), *menu_ptr); mwnd.state_.self_submenu = true; submenu_.child = &mwnd; submenu_.child->submenu_.parent = this; @@ -1042,7 +1038,7 @@ namespace nana API::set_window_z_order(handle(), mwnd.handle(), z_order_action::none); - mwnd.popup(*menu_ptr, state_.owner_menubar); + mwnd.popup(state_.owner_menubar); drawer.set_sub_window(true); if (forced) mwnd.goto_next(true); @@ -1099,7 +1095,7 @@ namespace nana }; drawerbase::menu::menu_builder mbuilder; - drawerbase::menu::menu_window * uiobj; + drawerbase::menu::menu_window * window_ptr; std::function destroy_answer; std::map sub_container; }; @@ -1107,7 +1103,7 @@ namespace nana menu::menu() :impl_(new implement) { - impl_->uiobj = nullptr; + impl_->window_ptr = nullptr; } menu::~menu() @@ -1120,34 +1116,36 @@ namespace nana delete impl_; } - auto menu::append(const std::string& text, const menu::event_fn_t& f) -> item_proxy + auto menu::append(const std::string& text, const event_fn_t& callback) -> item_proxy { - impl_->mbuilder.data().items.emplace_back(text, f); - return item_proxy(size() - 1, impl_->mbuilder.data().items.back()); + impl_->mbuilder.data().items.emplace_back(new item_type(text, callback)); + return item_proxy(size() - 1, *impl_->mbuilder.data().items.back()); } void menu::append_splitter() { - impl_->mbuilder.data().items.emplace_back(); + impl_->mbuilder.data().items.emplace_back(new item_type); } void menu::clear() { + internal_scope_guard lock; impl_->mbuilder.data().items.clear(); } void menu::enabled(std::size_t index, bool enable) { - impl_->mbuilder.data().items.at(index).flags.enabled = enable; + impl_->mbuilder.data().items.at(index)->flags.enabled = enable; } bool menu::enabled(std::size_t index) const { - return impl_->mbuilder.data().items.at(index).flags.enabled; + return impl_->mbuilder.data().items.at(index)->flags.enabled; } void menu::erase(std::size_t index) { + internal_scope_guard lock; auto & items = impl_->mbuilder.data().items; if(index < items.size()) items.erase(items.begin() + index); @@ -1155,7 +1153,7 @@ namespace nana void menu::image(std::size_t index, const paint::image& img) { - impl_->mbuilder.data().items.at(index).image = img; + impl_->mbuilder.data().items.at(index)->image = img; } bool menu::link(std::size_t index, menu& menu_obj) @@ -1196,22 +1194,22 @@ namespace nana void menu::popup(window wd, int x, int y) { - _m_popup(wd, x, y, false); + _m_popup(wd, { x, y }, false); } void menu::popup_await(window wd, int x, int y) { - _m_popup(wd, x, y, false); - if (impl_->uiobj) - API::wait_for(impl_->uiobj->handle()); + _m_popup(wd, { x, y }, false); + if (impl_->window_ptr) + API::wait_for(impl_->window_ptr->handle()); } void menu::close() { - if(impl_->uiobj) + if (impl_->window_ptr) { - impl_->uiobj->close(); - impl_->uiobj = nullptr; + impl_->window_ptr->close(); + impl_->window_ptr = nullptr; } } @@ -1227,17 +1225,17 @@ namespace nana bool menu::checked(std::size_t index) const { - return impl_->mbuilder.data().items.at(index).flags.checked; + return impl_->mbuilder.data().items.at(index)->flags.checked; } void menu::answerer(std::size_t index, const event_fn_t& fn) { - impl_->mbuilder.data().items.at(index).functor = fn; + impl_->mbuilder.data().items.at(index)->event_handler = fn; } - void menu::destroy_answer(const std::function& fn) + void menu::destroy_answer(std::function fn) { - impl_->destroy_answer = fn; + impl_->destroy_answer = std::move(fn); } void menu::gaps(const nana::point& pos) @@ -1247,18 +1245,18 @@ namespace nana void menu::goto_next(bool forward) { - if(impl_->uiobj) - impl_->uiobj->goto_next(forward); + if (impl_->window_ptr) + impl_->window_ptr->goto_next(forward); } bool menu::goto_submen() { - return (impl_->uiobj ? impl_->uiobj->submenu(true) : false); + return (impl_->window_ptr ? impl_->window_ptr->submenu(true) : false); } bool menu::exit_submenu() { - return (impl_->uiobj ? impl_->uiobj->submenu(false) : false); + return (impl_->window_ptr ? impl_->window_ptr->submenu(false) : false); } std::size_t menu::size() const @@ -1268,13 +1266,13 @@ namespace nana int menu::send_shortkey(wchar_t key) { - return (impl_->uiobj ? impl_->uiobj->send_shortkey(key) : 0); + return (impl_->window_ptr ? impl_->window_ptr->send_shortkey(key) : 0); } void menu::pick() { - if (impl_->uiobj) - impl_->uiobj->pick(); + if (impl_->window_ptr) + impl_->window_ptr->pick(); } menu& menu::max_pixels(unsigned px) @@ -1309,19 +1307,19 @@ namespace nana impl_->mbuilder.renderer(rd); } - void menu::_m_popup(window wd, int x, int y, bool called_by_menubar) + void menu::_m_popup(window wd, const point& pos, bool called_by_menubar) { if (impl_->mbuilder.data().items.size()) { close(); - impl_->uiobj = &(form_loader()(wd, false, point(x, y), &(*impl_->mbuilder.renderer()))); - impl_->uiobj->events().destroy.connect_unignorable([this]{ - impl_->uiobj = nullptr; + impl_->window_ptr = &(form_loader()(wd, false, pos, impl_->mbuilder, impl_->mbuilder.data())); + impl_->window_ptr->events().destroy.connect_unignorable([this](const arg_destroy&){ + impl_->window_ptr = nullptr; if (impl_->destroy_answer) impl_->destroy_answer(); }); - impl_->uiobj->popup(impl_->mbuilder.data(), called_by_menubar); + impl_->window_ptr->popup(called_by_menubar); } } //end class menu diff --git a/source/gui/widgets/menubar.cpp b/source/gui/widgets/menubar.cpp index 3d39b1f9..02a64da3 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -20,7 +20,7 @@ namespace nana public: static void popup(menu& m, window wd, int x, int y) { - m._m_popup(wd, x, y, true); + m._m_popup(wd, { x, y }, true); } };