diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index 8016836c..659f4316 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -176,7 +176,6 @@ namespace detail { caret_descriptor* caret; std::shared_ptr events_ptr; - general_events* attached_events; }together; widget_colors* scheme{ nullptr }; diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index 18c20ee5..1a4bf972 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -19,7 +19,6 @@ #include #include #include -#include namespace nana { @@ -63,14 +62,14 @@ namespace nana class basic_event : public detail::event_interface { public: - typedef const typename std::remove_reference::type & arg_reference; + using arg_reference = const typename std::remove_reference::type &; private: struct docker : public detail::docker_interface { basic_event * const event_ptr; std::function invoke; - bool flag_entered{ false }; + bool flag_deleted{ false }; bool unignorable{false}; @@ -92,6 +91,36 @@ namespace nana return event_ptr; } }; + + //class emit_counter is a RAII helper for emitting count + //It is used for avoiding a try{}catch block which is required for some finial works when + //event handlers throw exceptions. + class emit_counter + { + public: + emit_counter(basic_event* evt) + : evt_{evt} + { + ++evt->emitting_count_; + } + + ~emit_counter() + { + if ((0 == --evt_->emitting_count_) && evt_->deleted_flags_) + { + evt_->deleted_flags_ = false; + for (auto i = evt_->dockers_->begin(); i != evt_->dockers_->end();) + { + if (i->get()->flag_deleted) + i = evt_->dockers_->erase(i); + else + ++i; + } + } + } + private: + basic_event * const evt_; + }; public: /// It will get called firstly, because it is set at the beginning of the chain. template @@ -164,54 +193,37 @@ namespace nana return (nullptr == dockers_ ? 0 : dockers_->size()); } - void emit(arg_reference& arg) const + void emit(arg_reference& arg) { internal_scope_guard lock; if (nullptr == dockers_) return; - //Make a copy to allow create/destroy a new event handler when the call of emit in an event. - const std::size_t fixed_size = 10; - docker* fixed_buffer[fixed_size]; - docker** transitory = fixed_buffer; + emit_counter ec(this); - std::unique_ptr variable_buffer; auto& dockers = *dockers_; - if (dockers.size() > fixed_size) - { - variable_buffer.reset(new docker*[dockers.size()]); - transitory = variable_buffer.get(); - } + const auto dockers_len = dockers.size(); - auto output = transitory; - for (auto & dck : dockers) + //The dockers may resize when a new event handler is created by a calling handler. + //Traverses with position can avaid crash error which caused by a iterator which becomes invalid. + for (std::size_t pos = 0; pos < dockers_len; ++pos) { - (*output++) = dck.get(); - } - - bool stop_propagation = false; - for (; transitory != output; ++transitory) - { - auto docker_ptr = *transitory; - if (stop_propagation && !docker_ptr->unignorable) + auto docker_ptr = dockers[pos].get(); + if (docker_ptr->flag_deleted) continue; - auto i = std::find_if(dockers.begin(), dockers.end(), [docker_ptr](std::unique_ptr& p){ - return (docker_ptr == p.get()); - }); - - if (i != dockers.end()) + docker_ptr->invoke(arg); + if (arg.propagation_stopped()) { - docker_ptr->flag_entered = true; - docker_ptr->invoke(arg); + for (++pos; pos < dockers_len; ++pos) + { + auto docker_ptr = dockers[pos].get(); + if (!docker_ptr->unignorable || docker_ptr->flag_deleted) + continue; - if (arg.propagation_stopped()) - stop_propagation = true; - - docker_ptr->flag_entered = false; - - if (docker_ptr->flag_deleted) - dockers.erase(i); + docker_ptr->invoke(arg); + } + break; } } } @@ -228,17 +240,20 @@ namespace nana internal_scope_guard lock; if (dockers_) { - auto i = std::find_if(dockers_->begin(), dockers_->end(), [evt](const std::unique_ptr& sp) + for (auto i = dockers_->begin(), end = dockers_->end(); i != end; ++i) { - return (reinterpret_cast(evt) == sp.get()); - }); - - if (i != dockers_->end()) - { - if (i->get()->flag_entered) - i->get()->flag_deleted = true; - else - dockers_->erase(i); + if (reinterpret_cast(evt) == i->get()) + { + //Checks whether this event is working now. + if (emitting_count_ > 1) + { + i->get()->flag_deleted = true; + deleted_flags_ = true; + } + else + dockers_->erase(i); + break; + } } } } @@ -399,6 +414,8 @@ namespace nana } }; private: + unsigned emitting_count_{ 0 }; + bool deleted_flags_{ false }; std::unique_ptr>> dockers_; }; diff --git a/include/nana/gui/detail/handle_manager.hpp b/include/nana/gui/detail/handle_manager.hpp index 33bb97a6..91ac70f1 100644 --- a/include/nana/gui/detail/handle_manager.hpp +++ b/include/nana/gui/detail/handle_manager.hpp @@ -25,7 +25,6 @@ #include #include -#include namespace nana { @@ -260,9 +259,14 @@ namespace nana { if(cond_type::is_queue(handle)) { - auto i = std::find(queue.begin(), queue.end(), handle); - if(i != queue.end()) - queue.erase(i); + for (auto i = queue.begin(); i != queue.end(); ++i) + { + if (handle == *i) + { + queue.erase(i); + break; + } + } } } }; diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index 4cb500bf..96aec1fa 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -356,7 +356,6 @@ namespace nana effect.bground_fade_rate = 0; together.caret = nullptr; - together.attached_events = nullptr; extra_width = extra_height = 0; @@ -369,16 +368,15 @@ namespace nana bool basic_window::set_events(const std::shared_ptr& p) { - if (together.attached_events) + if (together.events_ptr) return false; together.events_ptr = p; - together.attached_events = p.get(); return true; } general_events * basic_window::get_events() const { - return together.attached_events; + return together.events_ptr.get(); } //end struct basic_window }//end namespace detail diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index be824a0b..76db85ac 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -146,6 +146,9 @@ namespace nana void bedrock::_m_emit_core(event_code evt_code, core_window_t* wd, bool draw_only, const ::nana::event_arg& event_arg) { + auto retain = wd->together.events_ptr; + auto evts_ptr = retain.get(); + switch (evt_code) { case event_code::click: @@ -162,35 +165,36 @@ namespace nana void(::nana::detail::drawer::*drawer_event_fn)(const arg_mouse&); ::nana::basic_event* evt_addr; + switch (evt_code) { case event_code::click: drawer_event_fn = &drawer::click; - evt_addr = &wd->together.attached_events->click; + evt_addr = &evts_ptr->click; break; case event_code::dbl_click: drawer_event_fn = &drawer::dbl_click; - evt_addr = &wd->together.attached_events->dbl_click; + evt_addr = &evts_ptr->dbl_click; break; case event_code::mouse_enter: drawer_event_fn = &drawer::mouse_enter; - evt_addr = &wd->together.attached_events->mouse_enter; + evt_addr = &evts_ptr->mouse_enter; break; case event_code::mouse_move: drawer_event_fn = &drawer::mouse_move; - evt_addr = &wd->together.attached_events->mouse_move; + evt_addr = &evts_ptr->mouse_move; break; case event_code::mouse_leave: drawer_event_fn = &drawer::mouse_leave; - evt_addr = &wd->together.attached_events->mouse_leave; + evt_addr = &evts_ptr->mouse_leave; break; case event_code::mouse_down: drawer_event_fn = &drawer::mouse_down; - evt_addr = &wd->together.attached_events->mouse_down; + evt_addr = &evts_ptr->mouse_down; break; case event_code::mouse_up: drawer_event_fn = &drawer::mouse_up; - evt_addr = &wd->together.attached_events->mouse_up; + evt_addr = &evts_ptr->mouse_up; break; default: throw std::runtime_error("Invalid mouse event code"); @@ -209,7 +213,7 @@ namespace nana { wd->drawer.mouse_wheel(*arg); if (!draw_only) - wd->together.attached_events->mouse_wheel.emit(*arg); + evts_ptr->mouse_wheel.emit(*arg); } break; } @@ -229,19 +233,19 @@ namespace nana { case event_code::key_press: drawer_event_fn = &drawer::key_press; - evt_addr = &wd->together.attached_events->key_press; + evt_addr = &evts_ptr->key_press; break; case event_code::key_char: drawer_event_fn = &drawer::key_char; - evt_addr = &wd->together.attached_events->key_char; + evt_addr = &evts_ptr->key_char; break; case event_code::key_release: drawer_event_fn = &drawer::key_release; - evt_addr = &wd->together.attached_events->key_release; + evt_addr = &evts_ptr->key_release; break; case event_code::shortkey: drawer_event_fn = &drawer::shortkey; - evt_addr = &wd->together.attached_events->shortkey; + evt_addr = &evts_ptr->shortkey; break; default: throw std::runtime_error("Invalid keyboard event code"); @@ -256,7 +260,7 @@ namespace nana { auto arg = dynamic_cast(&event_arg); if (arg) - wd->together.attached_events->expose.emit(*arg); + evts_ptr->expose.emit(*arg); } break; case event_code::focus: @@ -266,7 +270,7 @@ namespace nana { wd->drawer.focus(*arg); if (!draw_only) - wd->together.attached_events->focus.emit(*arg); + evts_ptr->focus.emit(*arg); } break; } @@ -277,7 +281,7 @@ namespace nana { wd->drawer.move(*arg); if (!draw_only) - wd->together.attached_events->move.emit(*arg); + evts_ptr->move.emit(*arg); } break; } @@ -288,7 +292,7 @@ namespace nana { wd->drawer.resizing(*arg); if (!draw_only) - wd->together.attached_events->resizing.emit(*arg); + evts_ptr->resizing.emit(*arg); } break; } @@ -299,7 +303,7 @@ namespace nana { wd->drawer.resized(*arg); if (!draw_only) - wd->together.attached_events->resized.emit(*arg); + evts_ptr->resized.emit(*arg); } break; } @@ -309,9 +313,9 @@ namespace nana auto arg = dynamic_cast(&event_arg); if (arg && (wd->other.category == category::flags::root)) { - auto evt_ptr = dynamic_cast(wd->together.attached_events); - if (evt_ptr) - evt_ptr->unload.emit(*arg); + auto evt_root = dynamic_cast(evts_ptr); + if (evt_root) + evt_root->unload.emit(*arg); } } break; @@ -320,7 +324,7 @@ namespace nana { auto arg = dynamic_cast(&event_arg); if (arg) - wd->together.attached_events->destroy.emit(*arg); + evts_ptr->destroy.emit(*arg); } break; default: diff --git a/source/gui/detail/linux_X11/bedrock.cpp b/source/gui/detail/linux_X11/bedrock.cpp index 1add84a6..2e40cc10 100644 --- a/source/gui/detail/linux_X11/bedrock.cpp +++ b/source/gui/detail/linux_X11/bedrock.cpp @@ -583,7 +583,7 @@ namespace detail delete msg.u.mouse_drop.files; arg.pos.x = msg.u.mouse_drop.x - msgwd->pos_root.x; arg.pos.y = msg.u.mouse_drop.y - msgwd->pos_root.y; - msgwd->together.attached_events->mouse_dropfiles.emit(arg); + msgwd->together.events_ptr->mouse_dropfiles.emit(arg); brock.wd_manager.do_lazy_refresh(msgwd, false); } break; @@ -766,7 +766,7 @@ namespace detail nana::point mspos{xevent.xbutton.x, xevent.xbutton.y}; while(msgwnd) { - if(msgwnd->together.attached_events->mouse_wheel.length() != 0) + if(msgwnd->together.events_ptr->mouse_wheel.length() != 0) { mspos -= msgwnd->pos_root; arg_wheel arg; @@ -810,27 +810,29 @@ namespace detail { if(hit) msgwnd->flags.action = mouse_action::over; + + auto retain = msgwnd->together.events_ptr; + auto evt_ptr = retain.get(); - auto events_ptr = msgwnd->together.events_ptr; arg.evt_code = event_code::mouse_up; emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); if(fire_click) { arg.evt_code = event_code::click; - msgwnd->together.attached_events->click.emit(arg); + evt_ptr->click.emit(arg); } if (brock.wd_manager.available(msgwnd)) { arg.evt_code = event_code::mouse_up; - msgwnd->together.attached_events->mouse_up.emit(arg); + evt_ptr->mouse_up.emit(arg); } } else if(fire_click) { arg.evt_code = event_code::click; - msgwnd->together.attached_events->click.emit(arg); + msgwnd->together.events_ptr->click.emit(arg); } brock.wd_manager.do_lazy_refresh(msgwnd, false); } @@ -1103,7 +1105,7 @@ namespace detail arg.window_handle = reinterpret_cast(msgwnd); brock.get_key_state(arg); - msgwnd->together.attached_events->key_char.emit(arg); + msgwnd->together.events_ptr->key_char.emit(arg); if(arg.ignore == false && brock.wd_manager.available(msgwnd)) brock.emit_drawer(event_code::key_char, msgwnd, arg, &context); } diff --git a/source/gui/detail/win32/bedrock.cpp b/source/gui/detail/win32/bedrock.cpp index f0c7eb1c..58c4eb6e 100644 --- a/source/gui/detail/win32/bedrock.cpp +++ b/source/gui/detail/win32/bedrock.cpp @@ -663,6 +663,7 @@ namespace detail case WM_SETFOCUS: case WM_KILLFOCUS: case WM_PAINT: + case WM_SYSCOMMAND: case WM_CLOSE: case WM_MOUSEACTIVATE: case WM_GETMINMAXINFO: @@ -851,7 +852,7 @@ namespace detail case WM_WINDOWPOSCHANGED: if ((reinterpret_cast(lParam)->flags & SWP_SHOWWINDOW) && (!msgwnd->visible)) brock.event_expose(msgwnd, true); - else if((reinterpret_cast(lParam)->flags & SWP_HIDEWINDOW) && msgwnd->visible) + else if ((reinterpret_cast(lParam)->flags & SWP_HIDEWINDOW) && msgwnd->visible) brock.event_expose(msgwnd, false); def_window_proc = true; @@ -871,6 +872,7 @@ namespace detail if (!brock.emit(event_code::focus, focus, arg, true, &context)) brock.wd_manager.set_focus(msgwnd, true); } + def_window_proc = true; break; case WM_KILLFOCUS: if(msgwnd->other.attribute.root->focus) @@ -891,10 +893,14 @@ namespace detail //focus_changed means that during an event procedure if the focus is changed if(brock.wd_manager.available(msgwnd)) msgwnd->root_widget->other.attribute.root->context.focus_changed = true; + + def_window_proc = true; break; case WM_MOUSEACTIVATE: if(msgwnd->flags.take_active == false) return MA_NOACTIVATE; + + def_window_proc = true; break; case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: pressed_wd = nullptr; @@ -1011,19 +1017,19 @@ namespace detail if (fire_click) { arg.evt_code = event_code::click; - msgwnd->together.attached_events->click.emit(arg); + retain->click.emit(arg); } if (brock.wd_manager.available(msgwnd)) { arg.evt_code = event_code::mouse_up; - msgwnd->together.attached_events->mouse_up.emit(arg); + retain->mouse_up.emit(arg); } } else if (fire_click) { arg.evt_code = event_code::click; - msgwnd->together.attached_events->click.emit(arg); + retain->click.emit(arg); } brock.wd_manager.do_lazy_refresh(msgwnd, false); } @@ -1114,7 +1120,7 @@ namespace detail auto evt_wd = scrolled_wd; while (evt_wd) { - if (evt_wd->together.attached_events->mouse_wheel.length() != 0) + if (evt_wd->together.events_ptr->mouse_wheel.length() != 0) { def_window_proc = false; nana::point mspos{ scr_pos.x, scr_pos.y }; @@ -1189,7 +1195,7 @@ namespace detail brock.wd_manager.calc_window_point(msgwnd, dropfiles.pos); dropfiles.window_handle = reinterpret_cast(msgwnd); - msgwnd->together.attached_events->mouse_dropfiles.emit(dropfiles); + msgwnd->together.events_ptr->mouse_dropfiles.emit(dropfiles); brock.wd_manager.do_lazy_refresh(msgwnd, false); } } @@ -1419,7 +1425,7 @@ namespace detail brock.get_key_state(arg); arg.ignore = false; - msgwnd->together.attached_events->key_char.emit(arg); + msgwnd->together.events_ptr->key_char.emit(arg); if ((false == arg.ignore) && brock.wd_manager.available(msgwnd)) brock.emit_drawer(event_code::key_char, msgwnd, arg, &context); @@ -1449,6 +1455,14 @@ namespace detail brock.delay_restore(2); //Restores while key release break; + case WM_SYSCOMMAND: + if (SC_TASKLIST == wParam) + { + int debug = 0; + //brock.close_menu_if_focus_other_window() + } + def_window_proc = true; + break; case WM_CLOSE: { arg_unload arg; @@ -1537,7 +1551,8 @@ namespace detail impl_->menu.taken_window = wd; //assigning of a nullptr taken window is to restore the focus of pre taken - if ((!wd) && pre) + //don't restore the focus if pre is a menu. + if ((!wd) && pre && (pre->root != get_menu())) { internal_scope_guard lock; wd_manager.set_focus(pre, false); diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 93186a57..e78da759 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -21,7 +21,6 @@ #include #include #include -#include namespace nana { @@ -192,7 +191,7 @@ namespace detail switch(evtid) { case event_code::mouse_drop: - wd->flags.dropable = (is_make || (0 != wd->together.attached_events->mouse_dropfiles.length())); + wd->flags.dropable = (is_make || (0 != wd->together.events_ptr->mouse_dropfiles.length())); break; default: break; @@ -985,11 +984,15 @@ namespace detail } else { - auto i = std::find_if(attr_cap.begin(), attr_cap.end(), - [wd](const std::pair & x){ return (x.first == wd);}); + for (auto i = attr_cap.begin(), end = attr_cap.end(); i != end; ++i) + { + if (i->first == wd) + { + attr_cap.erase(i); + break; + } + } - if(i != attr_cap.end()) - attr_cap.erase(i); return attr_.capture.window; } return wd; diff --git a/source/gui/timer.cpp b/source/gui/timer.cpp index dc4d53a2..75181a7f 100644 --- a/source/gui/timer.cpp +++ b/source/gui/timer.cpp @@ -109,11 +109,11 @@ namespace nana { public: #if defined(NANA_WINDOWS) - timer_core(timer_identifier tmid, const nana::basic_event& evt_elapse) + timer_core(timer_identifier tmid, basic_event& evt_elapse) : timer_(tmid), evt_elapse_(evt_elapse) {} #else - timer_core(const nana::basic_event& evt_elapse) + timer_core(basic_event& evt_elapse) : timer_(this), evt_elapse_(evt_elapse) {} #endif @@ -138,7 +138,7 @@ namespace nana } private: const timer_identifier timer_; - const nana::basic_event & evt_elapse_; + nana::basic_event & evt_elapse_; }; //end class timer_core #if defined(NANA_WINDOWS)