diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index b79ec168..bc0a116f 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -127,6 +127,9 @@ namespace detail basic_window * seek_non_lite_widget_ancestor() const; void set_action(mouse_action); + + /// Only refresh when the root of window is in lazy-updating mode + bool try_lazy_update(bool try_refresh); public: /// Override event_holder bool set_events(const std::shared_ptr&) override; @@ -176,8 +179,7 @@ namespace detail bool ignore_mouse_focus : 1; ///< A flag indicates whether the widget accepts focus when clicking on it bool space_click_enabled : 1; ///< A flag indicates whether enable mouse_down/click/mouse_up when pressing and releasing whitespace key. bool draggable : 1; - bool ignore_child_mapping : 1; - unsigned Reserved :16; + unsigned Reserved : 17; unsigned char tab; ///< indicate a window that can receive the keyboard TAB mouse_action action; mouse_action action_before; @@ -205,11 +207,14 @@ namespace detail { struct attr_root_tag { + bool ime_enabled{ false }; + bool lazy_update{ false }; ///< Indicates whether the window is in lazy-updating mode. + + container update_requesters; ///< Container for lazy-updating requesting windows. container tabstop; std::vector effects_edge_nimbus; basic_window* focus{nullptr}; basic_window* menubar{nullptr}; - bool ime_enabled{false}; cursor state_cursor{nana::cursor::arrow}; basic_window* state_cursor_window{ nullptr }; @@ -223,8 +228,6 @@ namespace detail update_state upd_state; dragdrop_status dnd_state{ dragdrop_status::not_ready }; - container mapping_requester; ///< Children which are ignored to mapping - union { attr_root_tag * root; diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index d3f36b27..d9c26326 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -120,7 +120,6 @@ namespace detail void refresh_tree(core_window_t*); void do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false); - void map_requester(core_window_t*); bool set_parent(core_window_t* wd, core_window_t* new_parent); core_window_t* set_focus(core_window_t*, bool root_has_been_focused, arg_focus::reason); diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index a52b2215..e52d402e 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -336,6 +336,43 @@ namespace nana flags.action = act; } + + bool basic_window::try_lazy_update(bool try_refresh) + { + if (drawer.graphics.empty()) + return true; + + if (!this->root_widget->other.attribute.root->lazy_update) + return false; + + if (nullptr == effect.bground) + { + if (try_refresh) + { + flags.refreshing = true; + drawer.refresh(); + flags.refreshing = false; + } + } + + for (auto i = this->root_widget->other.attribute.root->update_requesters.cbegin(); i != this->root_widget->other.attribute.root->update_requesters.cend();) + { + auto req = *i; + //Avoid redundancy, don't insert the window if it or its ancestor window already exist in the container. + if ((req == this) || req->is_ancestor_of(this)) + return true; + + //If there is a window which is a child or child's child of the window, remove it. + if (this->is_ancestor_of(req)) + i = this->root_widget->other.attribute.root->update_requesters.erase(i); + else + ++i; + } + + this->root_widget->other.attribute.root->update_requesters.push_back(this); + return true; + } + void basic_window::_m_init_pos_and_size(basic_window* parent, const rectangle& r) { pos_owner = pos_root = r.position(); @@ -390,7 +427,6 @@ namespace nana flags.ignore_menubar_focus = false; flags.ignore_mouse_focus = false; flags.space_click_enabled = false; - flags.ignore_child_mapping = false; visible = false; diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index 65d644ec..0cd1e796 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -641,13 +641,8 @@ namespace nana if (update_state::none == wd->other.upd_state) wd->other.upd_state = update_state::lazy; - auto ignore_mapping_value = wd->flags.ignore_child_mapping; - wd->flags.ignore_child_mapping = true; - _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); - wd->flags.ignore_child_mapping = ignore_mapping_value; - bool good_wd = false; if(wd_manager().available(wd)) { @@ -658,10 +653,7 @@ namespace nana wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); } else - { - wd_manager().map_requester(wd); wd->other.upd_state = update_state::none; - } good_wd = true; } diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 65e66701..9c96a7cd 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "inner_fwd_implement.hpp" #include @@ -555,6 +556,27 @@ namespace detail context.is_alt_pressed = false; } + class window_proc_guard + { + public: + window_proc_guard(detail::basic_window* wd) : + root_wd_(wd) + { + root_wd_->other.attribute.root->lazy_update = true; + } + + ~window_proc_guard() + { + if (!bedrock::instance().wd_manager().available(root_wd_)) + return; + + root_wd_->other.attribute.root->lazy_update = false; + root_wd_->other.attribute.root->update_requesters.clear(); + } + private: + detail::basic_window* const root_wd_; + }; + void window_proc_for_xevent(Display* /*display*/, XEvent& xevent) { typedef detail::bedrock::core_window_t core_window_t; @@ -569,10 +591,13 @@ namespace detail if(root_runtime) { - auto msgwnd = root_runtime->window; + auto const root_wd = root_runtime->window; + auto msgwnd = root_wd; + window_proc_guard wp_guard{ root_wd }; + auto& context = *brock.get_thread_context(msgwnd->thread_id); - auto pre_event_window = context.event_window; + auto const pre_event_window = context.event_window; auto pressed_wd = root_runtime->condition.pressed; auto pressed_wd_space = root_runtime->condition.pressed_by_space; auto hovered_wd = root_runtime->condition.hovered; @@ -1190,6 +1215,15 @@ namespace detail } } + if (wd_manager.available(root_wd) && root_wd->other.attribute.root->update_requesters.size()) + { + for (auto wd : root_wd->other.attribute.root->update_requesters) + { + window_layout::paint(wd, window_layout::paint_operation::have_refreshed, false); + wd_manager.map(wd, true); + } + } + root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) { diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index baece504..551cda69 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include "inner_fwd_implement.hpp" @@ -37,6 +38,7 @@ #include "bedrock_types.hpp" + typedef void (CALLBACK *win_event_proc_t)(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime); namespace nana @@ -738,6 +740,27 @@ namespace detail return static_cast(vkey); } + class window_proc_guard + { + public: + window_proc_guard(detail::basic_window* wd) : + root_wd_(wd) + { + root_wd_->other.attribute.root->lazy_update = true; + } + + ~window_proc_guard() + { + if (!bedrock::instance().wd_manager().available(root_wd_)) + return; + + root_wd_->other.attribute.root->lazy_update = false; + root_wd_->other.attribute.root->update_requesters.clear(); + } + private: + detail::basic_window* const root_wd_; + }; + LRESULT CALLBACK Bedrock_WIN32_WindowProc(HWND root_window, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT window_proc_value = 0; @@ -757,6 +780,7 @@ namespace detail bool def_window_proc = false; auto& context = *brock.get_thread_context(); + auto const pre_event_window = context.event_window; auto pressed_wd = root_runtime->condition.pressed; auto pressed_wd_space = root_runtime->condition.pressed_by_space; auto hovered_wd = root_runtime->condition.hovered; @@ -766,7 +790,10 @@ namespace detail pmdec.raw_param.wparam = wParam; internal_scope_guard lock; - auto msgwnd = root_runtime->window; + auto const root_wd = root_runtime->window; + auto msgwnd = root_wd; + + window_proc_guard wp_guard{ root_wd }; switch (message) { @@ -775,10 +802,7 @@ namespace detail { auto i = root_runtime->wpassoc->accel_commands.find(LOWORD(wParam)); if (i != root_runtime->wpassoc->accel_commands.end()) - { - auto fn = i->second; - fn(); - } + i->second(); } break; case WM_IME_STARTCOMPOSITION: @@ -1452,7 +1476,7 @@ namespace detail wd_manager.do_lazy_refresh(msgwnd, false); } } - return 0; + break; case WM_KEYUP: if(wParam != VK_MENU) //MUST NOT BE AN ALT { @@ -1553,13 +1577,28 @@ namespace detail def_window_proc = true; } + if (wd_manager.available(root_wd) && root_wd->other.attribute.root->update_requesters.size()) + { + for (auto wd : root_wd->other.attribute.root->update_requesters) + { + window_layout::paint(wd, window_layout::paint_operation::have_refreshed, false); + wd_manager.map(wd, true); + } + } + root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) { + context.event_window = pre_event_window; root_runtime->condition.pressed = pressed_wd; root_runtime->condition.hovered = hovered_wd; root_runtime->condition.pressed_by_space = pressed_wd_space; } + else + { + auto context = brock.get_thread_context(); + if(context) context->event_window = pre_event_window; + } if (!def_window_proc) return 0; diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index d23d2d05..12b5f86f 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -39,10 +39,6 @@ namespace nana } else _m_paint_glass_window(wd, (paint_operation::try_refresh == operation), req_refresh_children, false, true); - - //maproot and _m_paint_glass_window always copy the children graphics, therefore the mapping requester should - //be cleared to avoid redundant copying - wd->other.mapping_requester.clear(); } bool window_layout::maproot(core_window_t* wd, bool have_refreshed, bool req_refresh_children) diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 8a653939..0d7d0eb8 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -1001,28 +1001,7 @@ namespace detail //Thread-Safe Required! std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd) && !wd->is_draw_through()) - { - auto parent = wd->parent; - while (parent) - { - if(parent->flags.ignore_child_mapping || parent->flags.refreshing) - { - auto top = parent; - while(parent->parent) - { - parent = parent->parent; - if(parent->flags.ignore_child_mapping || parent->flags.refreshing) - top = parent; - } - - top->other.mapping_requester.push_back(wd); - return; - } - parent = parent->parent; - } - bedrock::instance().flush_surface(wd, forced, update_area); - } } //update @@ -1049,8 +1028,11 @@ namespace detail { if (!wd->flags.refreshing) { - window_layer::paint(wd, (redraw ? paint_operation::try_refresh : paint_operation::none), false); - this->map(wd, forced, update_area); + if (!wd->try_lazy_update(redraw)) + { + window_layer::paint(wd, (redraw ? paint_operation::try_refresh : paint_operation::none), false); + this->map(wd, forced, update_area); + } return true; } else if (forced) @@ -1097,46 +1079,30 @@ namespace detail { 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, (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); + if (!wd->try_lazy_update(wd->other.upd_state == core_window_t::update_state::request_refresh)) + { + 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) { //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) - this->map(wd, true); + { + if (!wd->try_lazy_update(false)) + this->map(wd, true); + } } - - //Map the requested children. - this->map_requester(wd); } else window_layer::paint(wd, paint_operation::try_refresh, refresh_tree); //only refreshing if it has an invisible parent } - else - wd->other.mapping_requester.clear(); wd->other.upd_state = core_window_t::update_state::none; } - void window_manager::map_requester(core_window_t* wd) - { - //Thread-Safe Required! - std::lock_guard lock(mutex_); - - if (false == impl_->wd_register.available(wd)) - return; - - if (wd->visible_parents()) - { - for(auto requestor : wd->other.mapping_requester) - this->map(requestor, true); - } - - wd->other.mapping_requester.clear(); - } - bool window_manager::set_parent(core_window_t* wd, core_window_t* newpa) { //Thread-Safe Required!