/** * Definition of General Events * Nana C++ Library(http://www.nanapro.org) * 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 * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/detail/general_events.hpp */ #ifndef NANA_DETAIL_GENERAL_EVENTS_HPP #define NANA_DETAIL_GENERAL_EVENTS_HPP #include #include #include "event_code.hpp" #include "internal_scope_guard.hpp" #include #include #include namespace nana { namespace detail { bool check_window(window); void events_operation_register(event_handle); class event_interface { public: virtual ~event_interface() = default; virtual void remove(event_handle) = 0; }; class docker_interface { public: virtual ~docker_interface() = default; virtual event_interface* get_event() const = 0; }; struct docker_base : public docker_interface { event_interface * event_ptr; bool flag_deleted{ false }; const bool unignorable; docker_base(event_interface*, bool unignorable_flag); detail::event_interface * get_event() const override; }; class event_base : public detail::event_interface { public: ~event_base(); std::size_t length() const; void clear() noexcept; void remove(event_handle evt) override; protected: //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. Precondition event_base.dockers_ != nullptr. class emit_counter { public: emit_counter(event_base*); ~emit_counter(); private: event_base * const evt_; }; event_handle _m_emplace(detail::docker_interface*, bool in_front); protected: unsigned emitting_count_{ 0 }; bool deleted_flags_{ false }; std::vector * dockers_{ nullptr }; }; }//end namespace detail /// base class for all event argument types class event_arg { public: virtual ~event_arg() = default; /// ignorable handlers behind the current one in a chain of event handlers will not get called. void stop_propagation() const; bool propagation_stopped() const; private: mutable bool stop_propagation_{ false }; }; struct general_events; /** @brief the type of the members of general_events. * * It connect the functions to be called as response to the event and manages that chain of responses * It is a functor, that get called to connect a "normal" response function, with normal "priority". * If a response function need another priority (unignorable or called first) it will need to be connected with * the specific connect function not with the operator() * It also permit to "emit" that event, calling all the active responders. */ template class basic_event : public detail::event_base { public: using arg_reference = const typename std::remove_reference::type &; private: struct docker : public detail::docker_base { /// the callback/response function taking the typed argument std::function invoke; docker(basic_event * evt, std::function && ivk, bool unignorable_flag) : docker_base(evt, unignorable_flag), invoke(std::move(ivk)) {} docker(basic_event * evt, const std::function & ivk, bool unignorable_flag) : docker_base(evt, unignorable_flag), invoke(ivk) {} }; public: /// Creates an event handler at the beginning of event chain template event_handle connect_front(Function && fn) { using prototype = typename std::remove_reference::type; return _m_emplace(new docker(this, factory::value>::build(std::forward(fn)), false), true); } /// It will not get called if stop_propagation() was called. event_handle connect(void (*fn)(arg_reference)) { return connect([fn](arg_reference arg){ fn(arg); }); } /// It will not get called if stop_propagation() was called, because it is set at the end of the chain.. template event_handle connect(Function && fn) { using prototype = typename std::remove_reference::type; return _m_emplace(new docker(this, factory::value>::build(std::forward(fn)), false), false); } /// It will not get called if stop_propagation() was called. template event_handle operator()(Function&& fn) { return connect(std::forward(fn)); } /// It will get called because it is unignorable. template event_handle connect_unignorable(Function && fn, bool in_front = false) { using prototype = typename std::remove_reference::type; return _m_emplace(new docker(this, factory::value>::build(std::forward(fn)), true), in_front); } void emit(arg_reference& arg, window window_handle) { internal_scope_guard lock; if (nullptr == dockers_) return; emit_counter ec(this); //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. auto i = dockers_->data(); auto const end = i + dockers_->size(); for (; i != end; ++i) { if (static_cast(*i)->flag_deleted) continue; static_cast(*i)->invoke(arg); if (window_handle && (!detail::check_window(window_handle))) break; if (arg.propagation_stopped()) { for (++i; i != end; ++i) { if (!static_cast(*i)->unignorable || static_cast(*i)->flag_deleted) continue; static_cast(*i)->invoke(arg); if (window_handle && (!detail::check_window(window_handle))) break; } break; } } } private: template struct factory { static std::function build(Fn && fn) { return std::move(fn); } static std::function build(const Fn & fn) { return fn; } }; template struct factory { typedef typename std::remove_reference::type arg_type; typedef typename std::remove_reference::type fn_type; template static std::function build(Tfn && fn) { typedef typename std::remove_reference::type type; return build_second(std::forward(fn), &type::operator()); } template static std::function build_second(Tfn&& fn, Ret(fn_type::*)()) { return [fn](arg_reference) mutable { fn(); }; } template static std::function build_second(Tfn&& fn, Ret(fn_type::*)()const) { return [fn](arg_reference) mutable { fn(); }; } static std::function build_second(fn_type&& fn, void(fn_type::*)(arg_reference)) { return std::move(fn); } static std::function build_second(fn_type&& fn, void(fn_type::*)(arg_reference) const) { return std::move(fn); } static std::function build_second(fn_type& fn, void(fn_type::*)(arg_reference)) { return fn; } static std::function build_second(fn_type& fn, void(fn_type::*)(arg_reference) const) { return fn; } static std::function build_second(const fn_type& fn, void(fn_type::*)(arg_reference)) { return fn; } static std::function build_second(const fn_type& fn, void(fn_type::*)(arg_reference) const) { return fn; } template static std::function build_second(Tfn&& fn, Ret(fn_type::*)(Arg2)) { static_assert(std::is_convertible::value, "The parameter type is not allowed, please check the function parameter type where you connected the event function."); return[fn](arg_reference arg) mutable { fn(arg); }; } template static std::function build_second(Tfn&& fn, Ret(fn_type::*)(Arg2)const) { static_assert(std::is_convertible::value, "The parameter type is not allowed, please check the function parameter type where you connected the event function."); return [fn](arg_reference arg) mutable { fn(arg); }; } }; template struct factory < std::function, false> { typedef typename std::remove_reference::type arg_type; static_assert(std::is_convertible::value, "The parameter type is not allowed, please check the function parameter type where you connected the event function."); static std::function build(const std::function& fn) { return [fn](arg_reference arg) mutable{ fn(arg); }; } static std::function build_second(std::function && fn) { return std::move(fn); } }; template struct factory < std::function, false> { static std::function build(const std::function& fn) { return[fn](arg_reference) mutable{ fn(); }; } }; template struct factory < Ret(*)(), false> { static std::function build(Ret(*fn)()) { return[fn](arg_reference) mutable{ fn(); }; } }; template struct factory < Ret(*)(Arg2), false> { typedef typename std::remove_reference::type arg_type; static_assert(std::is_convertible::value, "The parameter type is not allowed, please check the function parameter type where you connected the event function."); static std::function build(Ret(*fn)(Arg2)) { return[fn](arg_reference arg) mutable { fn(arg); }; } }; template struct factory < Ret(), false> { static std::function build(Ret(*fn)()) { return[fn](arg_reference){ fn(); }; } }; template struct factory < Ret(Arg2), false> { typedef typename std::remove_reference::type arg_type; static_assert(std::is_convertible::value, "The parameter type is not allowed, please check the function parameter type where you connected the event function."); static std::function build(Ret(*fn)(Arg)) { return[fn](arg_reference arg){ fn(arg); }; } }; }; struct arg_mouse : public event_arg { event_code evt_code; ///< what kind of mouse event? ::nana::window window_handle; ///< A handle to the event window ::nana::point pos; ///< cursor position in the event window ::nana::mouse button; ///< indicates a button which triggers the event bool left_button; ///< true if mouse left button is pressed bool mid_button; ///< true if mouse middle button is pressed bool right_button; ///< true if mouse right button is pressed bool alt; ///< true if keyboard alt is pressed bool shift; ///< true if keyboard Shift is pressed bool ctrl; ///< true if keyboard Ctrl is pressed /// Checks if left button is operated, bool is_left_button() const { return (event_code::mouse_move == evt_code ? left_button : (mouse::left_button == button)); } }; /// \brief in arg_wheel event_code is event_code::mouse_wheel /// The type arg_wheel is derived from arg_mouse, a handler /// with prototype void(const arg_mouse&) can be set for mouse_wheel. struct arg_wheel : public arg_mouse { enum class wheel{ vertical, horizontal }; wheel which; ///< which wheel is rotated bool upwards; ///< true if the wheel is rotated to the top/left, depends on which and false otherwise unsigned distance; ///< expressed in multiples or divisions of 120 }; struct arg_dropfiles : public event_arg { ::nana::window window_handle; ///< A handle to the event window ::nana::point pos; ///< cursor position in the event window std::vector files; ///< external filenames }; struct arg_expose : public event_arg { ::nana::window window_handle; ///< A handle to the event window bool exposed; ///< the window is visible? }; struct arg_focus : public event_arg { /// A constant to indicate how keyboard focus emitted. enum class reason { general, ///< the focus is received by OS native window manager. tabstop, ///< the focus is received by pressing tab. mouse_press ///< the focus is received by pressing a mouse button. }; ::nana::window window_handle; ///< A handle to the event window ::nana::native_window_type receiver; ///< it is a native window handle, and specified which window receives focus bool getting; ///< the window received focus? reason focus_reason; ///< determines how the widget receives keyboard focus, it is ignored when 'getting' is equal to false }; struct arg_keyboard : public event_arg { event_code evt_code; ///< it is event_code::key_press in current event ::nana::window window_handle; ///< A handle to the event window mutable wchar_t key; ///< the key corresponding to the key pressed mutable bool ignore; ///< this member is only available for key_char event, set 'true' to ignore the input. bool ctrl; ///< keyboard Ctrl is pressed? bool shift; ///< keyboard Shift is pressed }; struct arg_move : public event_arg { ::nana::window window_handle; ///< A handle to the event window int x; ///< int y; ///< }; struct arg_resized : public event_arg { ::nana::window window_handle; ///< A handle to the event window unsigned width; ///< new width in pixels. unsigned height; ///< new height in pixels. }; struct arg_resizing : public event_arg { ::nana::window window_handle; ///< A handle to the event window window_border border; ///< the window is being resized by moving border mutable unsigned width; ///< new width in pixels. If it is modified, the window's width will be the modified value mutable unsigned height; ///< new height in pixels. If it is modified, the window's height will be the modified value }; struct arg_unload : public event_arg { ::nana::window window_handle; ///< A handle to the event window mutable bool cancel; ///< }; struct arg_destroy : public event_arg { ::nana::window window_handle; ///< A handle to the event window }; /// a higher level event argument than just mouse down struct arg_click : public event_arg { ::nana::window window_handle; ///< A handle to the event window const arg_mouse* mouse_args{}; ///< If it is not null, it refers to the mouse arguments for click event emitted by mouse, nullptr otherwise. }; /// provides some fundamental events that every widget owns. struct general_events { virtual ~general_events() = default; basic_event mouse_enter; ///< the cursor enters the window basic_event mouse_move; ///< the cursor moves on the window basic_event mouse_leave; ///< the cursor leaves the window basic_event mouse_down; ///< the user presses the mouse button basic_event mouse_up; ///< the user presses the mouse button basic_event click; ///< the window is clicked, but occurs after mouse_down and before mouse_up basic_event dbl_click; ///< the window is double clicked basic_event mouse_wheel; ///< the mouse wheel rotates while the window has focus basic_event mouse_dropfiles; ///< the mouse drops some external data while the window enable accepting files basic_event expose; ///< the visibility changes basic_event focus; ///< the window receives or loses keyboard focus basic_event key_press; ///< a key is pressed while the window has focus. event code is event_code::key_press basic_event key_release; ///< a key is released while the window has focus. event code is event_code::key_release basic_event key_char; ///< a character, whitespace or backspace is pressed. event code is event_code::key_char basic_event shortkey; ///< a defined short key is pressed. event code is event_code::shortkey basic_event move; ///< the window changes position basic_event resizing; ///< the window is changing its size basic_event resized; ///< the window is changing its size basic_event destroy; ///< the window is destroyed, but occurs when all children have been destroyed }; namespace detail { struct events_root_extension : public general_events { basic_event unload; }; }//end namespace detail }//end namespace nana #include #endif