From 457d86aa39dacb16e909d8f2529c6da3c0328684 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 24 Feb 2018 06:13:46 +0800 Subject: [PATCH] implement keyboard accelerators for linux --- source/gui/detail/bedrock_posix.cpp | 93 +++++++++++++++++++++++---- source/gui/detail/bedrock_windows.cpp | 3 +- 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 131d42de..b8edb34c 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -104,9 +104,40 @@ namespace detail void window_proc_for_packet(Display *, nana::detail::msg_packet_tag&); void window_proc_for_xevent(Display*, XEvent&); + class accel_key_comparer + { + public: + bool operator()(const accel_key& a, const accel_key& b) const + { + auto va = a.case_sensitive ? a.key : std::tolower(a.key); + auto vb = b.case_sensitive ? b.key : std::tolower(b.key); + + if(va < vb) + return true; + else if(va > vb) + return false; + + if (a.case_sensitive != b.case_sensitive) + return b.case_sensitive; + + if (a.alt != b.alt) + return b.alt; + + if (a.ctrl != b.ctrl) + return b.ctrl; + + return ((a.shift != b.shift) && b.shift); + } + }; + + struct accel_key_value + { + std::function command; + }; + struct window_platform_assoc { - //Wait... + std::map accel_commands; }; //class bedrock defines a static object itself to implement a static singleton @@ -233,9 +264,16 @@ namespace detail delete passoc; } - void bedrock::keyboard_accelerator(native_window_type, const accel_key&, const std::function& fn) + void bedrock::keyboard_accelerator(native_window_type wd, const accel_key& ackey, const std::function& fn) { + auto misc = wd_manager().root_runtime(wd); + if (nullptr == misc) + return; + if (!misc->wpassoc) + misc->wpassoc = new window_platform_assoc; + + misc->wpassoc->accel_commands[ackey].command = fn; } element_store& bedrock::get_element_store() const @@ -506,6 +544,34 @@ namespace detail return wchar_t(keysym); } + bool translate_keyboard_accelerator(root_misc* misc, char os_code, const arg_keyboard& modifiers) + { + if(!misc->wpassoc) + return false; + + auto lower_oc = std::tolower(os_code); + + std::function command; + + for(auto & accel : misc->wpassoc->accel_commands) + { + if(accel.first.key != (accel.first.case_sensitive ? os_code : lower_oc)) + continue; + + if(accel.first.alt == modifiers.alt && accel.first.ctrl == modifiers.ctrl && accel.first.shift == modifiers.shift) + { + command = accel.second.command; + break; + } + } + + if(!command) + return false; + + command(); + return true; + } + void window_proc_for_xevent(Display* /*display*/, XEvent& xevent) { typedef detail::bedrock::core_window_t core_window_t; @@ -865,6 +931,9 @@ namespace detail if(msgwnd) { + arg_keyboard modifiers_status; + brock.get_key_state(modifiers_status); + KeySym keysym; Status status; char fixbuf[33]; @@ -899,16 +968,20 @@ namespace detail keybuf[len] = 0; wchar_t os_code = 0; + bool accel_translated = false; + switch(status) { case XLookupKeySym: case XLookupBoth: os_code = os_code_from_keysym(keysym); + accel_translated = translate_keyboard_accelerator(root_runtime, os_code, modifiers_status); + if(accel_translated) + break; + if(os_code == keyboard::tab && (false == (msgwnd->flags.tab & detail::tab_type::eating))) //Tab { - arg_keyboard argkey; - brock.get_key_state(argkey); - auto tstop_wd = wd_manager.tabstop(msgwnd, !argkey.shift); + auto tstop_wd = wd_manager.tabstop(msgwnd, !modifiers_status.shift); if (tstop_wd) { root_runtime->condition.ignore_tab = true; @@ -923,9 +996,9 @@ namespace detail if((nullptr == pressed_wd) && (nullptr == pressed_wd_space)) { arg_mouse arg; - arg.alt = false; + arg.alt = modifiers_status.alt; arg.button = ::nana::mouse::left_button; - arg.ctrl = false; + arg.ctrl = modifiers_status.ctrl; arg.evt_code = event_code::mouse_down; arg.left_button = true; arg.mid_button = false; @@ -971,11 +1044,10 @@ namespace detail if(keyboard::os_ctrl == os_code) context.is_ctrl_pressed = true; - arg_keyboard arg; + arg_keyboard arg = modifiers_status; arg.ignore = false; arg.key = os_code; arg.evt_code = event_code::key_press; - brock.get_key_state(arg); arg.window_handle = reinterpret_cast(msgwnd); brock.emit(event_code::key_press, msgwnd, arg, true, &context); @@ -1004,7 +1076,7 @@ namespace detail for(int i = 0; i < len; ++i) { - arg_keyboard arg; + arg_keyboard arg = modifiers_status; arg.ignore = false; arg.key = charbuf[i]; @@ -1027,7 +1099,6 @@ namespace detail } arg.evt_code = event_code::key_char; arg.window_handle = reinterpret_cast(msgwnd); - brock.get_key_state(arg); msgwnd->annex.events_ptr->key_char.emit(arg, reinterpret_cast(msgwnd)); if(arg.ignore == false && wd_manager.available(msgwnd)) draw_invoker(&drawer::key_char, msgwnd, arg, &context); diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 96aaed8f..d1267332 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -1650,8 +1650,7 @@ namespace detail auto misc = wd_manager().root_runtime(wd); if (nullptr == misc) return; - - auto wpassoc = misc->wpassoc; + if (!misc->wpassoc) misc->wpassoc = new window_platform_assoc;