From 569eb49a5ca01b5de58348c4f055b27f9e927149 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 27 Feb 2016 02:02:29 +0800 Subject: [PATCH] fix and improve the internal handle of focus change enhanced textbox behavior of focus change --- include/nana/gui/basis.hpp | 1 + include/nana/gui/detail/general_events.hpp | 15 ++- .../nana/gui/detail/inner_fwd_implement.hpp | 3 +- include/nana/gui/detail/window_manager.hpp | 2 +- include/nana/gui/programming_interface.hpp | 6 + .../gui/widgets/skeletons/text_editor.hpp | 12 +- .../widgets/skeletons/text_editor_part.hpp | 9 ++ include/nana/gui/widgets/textbox.hpp | 10 ++ source/gui/detail/bedrock_posix.cpp | 103 ++++++++------- source/gui/detail/bedrock_windows.cpp | 102 ++++++++------- source/gui/detail/window_manager.cpp | 4 +- source/gui/programming_interface.cpp | 4 +- source/gui/widgets/skeletons/text_editor.cpp | 122 +++++++++++++++--- source/gui/widgets/textbox.cpp | 27 ++-- 14 files changed, 290 insertions(+), 130 deletions(-) diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index 26ca8885..723d3140 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -95,6 +95,7 @@ namespace nana undo = substitute, //System Code for OS + os_tab = 0x09, os_shift = 0x10, os_ctrl = 0x11, os_pageup = 0x21, os_pagedown, diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index 0ff84083..c49cab98 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -424,9 +424,18 @@ namespace nana struct arg_focus : public event_arg { - ::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? + /// 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 diff --git a/include/nana/gui/detail/inner_fwd_implement.hpp b/include/nana/gui/detail/inner_fwd_implement.hpp index 90589d3c..e24853b0 100644 --- a/include/nana/gui/detail/inner_fwd_implement.hpp +++ b/include/nana/gui/detail/inner_fwd_implement.hpp @@ -117,8 +117,9 @@ namespace nana{ nana::paint::graphics root_graph; shortkey_container shortkeys; - struct condition_tag + struct condition_rep { + bool ignore_tab{ false }; //ignore tab when the focus is changed by pressing tab. core_window_t* pressed{nullptr}; //The handle to a window which has been pressed by pressing left button of mouse. core_window_t* pressed_by_space{ nullptr }; //The handle to a window which has been pressed by pressing spacebar. core_window_t* hovered{nullptr}; //the latest window that mouse moved diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index b04c32dc..965be995 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -136,7 +136,7 @@ namespace detail std::vector get_children(core_window_t*) const; 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); + core_window_t* set_focus(core_window_t*, bool root_has_been_focused, arg_focus::reason); core_window_t* capture_redirect(core_window_t*); void capture_ignore_children(bool ignore); diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index c50f8f2e..03138a04 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -283,8 +283,14 @@ namespace API cursor window_cursor(window); void activate_window(window); + + /// Determines whether the specified window will get the keyboard focus when its root window gets native system focus. bool is_focus_ready(window); + + /// Returns the current keyboard focus window. window focus_window(); + + /// Sets the keyboard focus for a specified window. void focus_window(window); window capture_window(); diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 3189cd18..986bc438 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -229,6 +229,9 @@ namespace nana{ namespace widgets /// Returns text position of each line that currently displays on screen const std::vector& text_position() const; + + void focus_behavior(text_focus_behavior); + void select_behavior(bool move_to_end); public: void draw_corner(); void render(bool focused); @@ -249,6 +252,8 @@ namespace nana{ namespace widgets const upoint& caret() const; point caret_screen_pos() const; bool scroll(bool upwards, bool vertical); + + bool focus_changed(const arg_focus&); bool mouse_enter(bool); bool mouse_move(bool left_button, const point& screen_pos); bool mouse_pressed(const arg_mouse& arg); @@ -353,10 +358,13 @@ namespace nana{ namespace widgets struct selection { - enum mode_selection_t{mode_no_selected, mode_mouse_selected, mode_method_selected}; + enum class mode{ no_selected, mouse_selected, method_selected }; - mode_selection_t mode_selection; + text_focus_behavior behavior; + bool move_to_end; + mode mode_selection; bool dragged; + bool ignore_press; nana::upoint a, b; }select_; diff --git a/include/nana/gui/widgets/skeletons/text_editor_part.hpp b/include/nana/gui/widgets/skeletons/text_editor_part.hpp index be4ec920..8cf1c389 100644 --- a/include/nana/gui/widgets/skeletons/text_editor_part.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor_part.hpp @@ -11,6 +11,15 @@ namespace nana { namespace skeletons { + enum class text_focus_behavior + { + none, + select, + select_if_tabstop, + select_if_click, + select_if_tabstop_or_click + }; + //forward declaration class text_editor; diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 69fda830..bc3b9ab1 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -98,6 +98,8 @@ namespace nana :public widget_object { public: + using text_focus_behavior = widgets::skeletons::text_focus_behavior; + using text_positions = std::vector; /// The default constructor without creating the widget. textbox(); @@ -207,6 +209,14 @@ namespace nana /// Returns the height of line in pixels unsigned line_pixels() const; + + /// Sets the behavior when textbox gets focus. + void focus_behavior(text_focus_behavior); + + /// Sets the caret move behavior when the content of textbox is selected. + /// E.g. Whether caret moves to left of selected content or moves to left of last position when left arrow key is pressed. + /// @param move_to_end determines whether to move caret to left of selected_content or to left of last position. + void select_behavior(bool move_to_end); protected: //Overrides widget's virtual functions native_string_type _m_caption() const throw() override; diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index a8f78c37..d5b9a282 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -255,7 +255,7 @@ namespace detail if ((!wd) && pre) { internal_scope_guard lock; - wd_manager().set_focus(pre, false); + wd_manager().set_focus(pre, false, arg_focus::reason::general); wd_manager().update(pre, true, false); } } @@ -479,6 +479,7 @@ namespace detail arg.window_handle = reinterpret_cast(wd); arg.receiver = recv; arg.getting = getting; + arg.focus_reason = arg_focus::reason::general; } void assign_arg(arg_wheel& arg, basic_window* wd, const XEvent& evt) @@ -701,7 +702,7 @@ namespace detail arg.receiver = native_window; arg.getting = true; if(!brock.emit(event_code::focus, focus, arg, true, &context)) - brock.wd_manager().set_focus(msgwnd, true); + brock.wd_manager().set_focus(msgwnd, true, arg_focus::reason::general); } break; case FocusOut: @@ -765,7 +766,7 @@ namespace detail if (new_focus && !new_focus->flags.ignore_mouse_focus) { context.event_window = new_focus; - auto kill_focus = brock.wd_manager().set_focus(new_focus, false); + auto kill_focus = brock.wd_manager().set_focus(new_focus, false, arg_focus::reason::mouse_press); if (kill_focus != new_focus) brock.wd_manager().do_lazy_refresh(kill_focus, false); } @@ -1048,7 +1049,8 @@ namespace detail auto tstop_wd = wd_manager.tabstop(msgwnd, !argkey.shift); if (tstop_wd) { - wd_manager.set_focus(tstop_wd, false); + root_runtime->condition.ignore_tab = true; + wd_manager.set_focus(tstop_wd, false, arg_focus::reason::tabstop); wd_manager.do_lazy_refresh(msgwnd, false); wd_manager.do_lazy_refresh(tstop_wd, true); } @@ -1144,8 +1146,8 @@ namespace detail arg.ignore = false; arg.key = charbuf[i]; - // When tab is pressed, only tab-eating mode is allowed - if ((keyboard::tab == arg.key) && !(msgwnd->flags.tab & tab_type::eating)) + //Only accept tab when it is not ignored. + if ((keyboard::tab == arg.key) && root_runtime->condition.ignore_tab) continue; if(context.is_alt_pressed) @@ -1185,52 +1187,61 @@ namespace detail nana::detail::platform_spec::instance().write_keystate(xevent.xkey); { auto os_code = os_code_from_keysym(::XLookupKeysym(&xevent.xkey, 0)); - if(keyboard::alt != os_code) + if(keyboard::alt != os_code) //MUST NOT BE AN ALT { if(0x11 == os_code) context.is_ctrl_pressed = false; - msgwnd = brock.focus(); - if(msgwnd) - { - if(msgwnd == pressed_wd_space) - { - msgwnd->flags.action = mouse_action::normal; + if (('\t' == os_code) && root_runtime->condition.ignore_tab) + { + root_runtime->condition.ignore_tab = false; + } + else + { + + msgwnd = brock.focus(); + if(msgwnd) + { + if(msgwnd == pressed_wd_space) + { + msgwnd->flags.action = mouse_action::normal; - arg_click click_arg; - click_arg.mouse_args = nullptr; - click_arg.window_handle = reinterpret_cast(msgwnd); + arg_click click_arg; + click_arg.mouse_args = nullptr; + click_arg.window_handle = reinterpret_cast(msgwnd); - auto retain = msgwnd->together.events_ptr; - if (brock.emit(event_code::click, msgwnd, click_arg, true, &context)) - { - arg_mouse arg; - arg.alt = false; - arg.button = ::nana::mouse::left_button; - arg.ctrl = false; - arg.evt_code = event_code::mouse_up; - arg.left_button = true; - arg.mid_button = false; - arg.pos.x = 0; - arg.pos.y = 0; - arg.window_handle = reinterpret_cast(msgwnd); + auto retain = msgwnd->together.events_ptr; + if (brock.emit(event_code::click, msgwnd, click_arg, true, &context)) + { + arg_mouse arg; + arg.alt = false; + arg.button = ::nana::mouse::left_button; + arg.ctrl = false; + arg.evt_code = event_code::mouse_up; + arg.left_button = true; + arg.mid_button = false; + arg.pos.x = 0; + arg.pos.y = 0; + arg.window_handle = reinterpret_cast(msgwnd); - emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); - brock.wd_manager().do_lazy_refresh(msgwnd, false); - } - pressed_wd_space = nullptr; - } - else - { - arg_keyboard arg; - arg.evt_code = event_code::key_release; - arg.window_handle = reinterpret_cast(msgwnd); - arg.ignore = false; - arg.key = os_code; - brock.get_key_state(arg); - brock.emit(event_code::key_release, msgwnd, arg, true, &context); - } - } + emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); + brock.wd_manager().do_lazy_refresh(msgwnd, false); + } + pressed_wd_space = nullptr; + } + else + { + arg_keyboard arg; + + arg.evt_code = event_code::key_release; + arg.window_handle = reinterpret_cast(msgwnd); + arg.ignore = false; + arg.key = os_code; + brock.get_key_state(arg); + brock.emit(event_code::key_release, msgwnd, arg, true, &context); + } + } + } if(os_code < keyboard::os_arrow_left || keyboard::os_arrow_down < os_code) brock.delay_restore(2); //Restores while key release @@ -1245,7 +1256,7 @@ namespace detail { bool set_focus = (brock.focus() != msgwnd) && (!msgwnd->root_widget->flags.ignore_menubar_focus); if (set_focus) - brock.wd_manager().set_focus(msgwnd, false); + brock.wd_manager().set_focus(msgwnd, false, arg_focus::reason::general); arg_keyboard arg; arg.evt_code = event_code::key_release; diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 1f557bca..8b3cb978 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -549,6 +549,7 @@ namespace detail arg.window_handle = reinterpret_cast(wd); arg.receiver = recv; arg.getting = getting; + arg.focus_reason = arg_focus::reason::general; } void assign_arg(arg_wheel& arg, basic_window* wd, const parameter_decoder& pmdec) @@ -882,7 +883,7 @@ namespace detail arg_focus arg; assign_arg(arg, focus, native_window, true); if (!brock.emit(event_code::focus, focus, arg, true, &context)) - brock.wd_manager().set_focus(msgwnd, true); + brock.wd_manager().set_focus(msgwnd, true, arg_focus::reason::general); } def_window_proc = true; break; @@ -922,7 +923,7 @@ namespace detail { if (msgwnd->flags.take_active && !msgwnd->flags.ignore_mouse_focus) { - auto killed = brock.wd_manager().set_focus(msgwnd, false); + auto killed = brock.wd_manager().set_focus(msgwnd, false, arg_focus::reason::mouse_press); if (killed != msgwnd) brock.wd_manager().do_lazy_refresh(killed, false); } @@ -964,7 +965,7 @@ namespace detail auto new_focus = (msgwnd->flags.take_active ? msgwnd : msgwnd->other.active_window); if (new_focus && (!new_focus->flags.ignore_mouse_focus)) { - auto kill_focus = brock.wd_manager().set_focus(new_focus, false); + auto kill_focus = brock.wd_manager().set_focus(new_focus, false, arg_focus::reason::mouse_press); if (kill_focus != new_focus) brock.wd_manager().do_lazy_refresh(kill_focus, false); } @@ -1383,7 +1384,7 @@ namespace detail bool set_focus = (brock.focus() != msgwnd) && (!msgwnd->root_widget->flags.ignore_menubar_focus); if (set_focus) - brock.wd_manager().set_focus(msgwnd, false); + brock.wd_manager().set_focus(msgwnd, false, arg_focus::reason::general); arg_keyboard arg; arg.evt_code = event_code::key_release; @@ -1414,14 +1415,16 @@ namespace detail if(msgwnd) { auto & wd_manager = brock.wd_manager(); - if((VK_TAB == wParam) && (!msgwnd->visible || (false == (msgwnd->flags.tab & tab_type::eating)))) //Tab + + if ((VK_TAB == wParam) && (!msgwnd->visible || (false == (msgwnd->flags.tab & tab_type::eating)))) //Tab { bool is_forward = (::GetKeyState(VK_SHIFT) >= 0); auto tstop_wd = wd_manager.tabstop(msgwnd, is_forward); if (tstop_wd) { - wd_manager.set_focus(tstop_wd, false); + root_runtime->condition.ignore_tab = true; + wd_manager.set_focus(tstop_wd, false, arg_focus::reason::tabstop); wd_manager.do_lazy_refresh(msgwnd, false); wd_manager.do_lazy_refresh(tstop_wd, true); } @@ -1479,8 +1482,10 @@ namespace detail msgwnd = brock.focus(); if (msgwnd && msgwnd->flags.enabled) { - // When tab is pressed, only tab-eating mode is allowed - if ((9 != wParam) || (msgwnd->flags.tab & tab_type::eating)) + auto & wd_manager = brock.wd_manager(); + + //Only accept tab when it is not ignored. + if (VK_TAB != wParam || !root_runtime->condition.ignore_tab) { arg_keyboard arg; arg.evt_code = event_code::key_char; @@ -1490,55 +1495,62 @@ namespace detail arg.ignore = false; msgwnd->together.events_ptr->key_char.emit(arg); - if ((false == arg.ignore) && brock.wd_manager().available(msgwnd)) + if ((false == arg.ignore) && wd_manager.available(msgwnd)) brock.emit_drawer(event_code::key_char, msgwnd, arg, &context); - brock.wd_manager().do_lazy_refresh(msgwnd, false); + wd_manager.do_lazy_refresh(msgwnd, false); } } return 0; case WM_KEYUP: if(wParam != VK_MENU) //MUST NOT BE AN ALT { - msgwnd = brock.focus(); - if(msgwnd) + if (VK_TAB == wParam && root_runtime->condition.ignore_tab) { - if (msgwnd == pressed_wd_space) + root_runtime->condition.ignore_tab = false; + } + else + { + msgwnd = brock.focus(); + if (msgwnd) { - msgwnd->flags.action = mouse_action::normal; - - arg_click click_arg; - click_arg.mouse_args = nullptr; - click_arg.window_handle = reinterpret_cast(msgwnd); - - auto retain = msgwnd->together.events_ptr; - if (brock.emit(event_code::click, msgwnd, click_arg, true, &context)) + if (msgwnd == pressed_wd_space) { - arg_mouse arg; - arg.alt = false; - arg.button = ::nana::mouse::left_button; - arg.ctrl = false; - arg.evt_code = event_code::mouse_up; - arg.left_button = true; - arg.mid_button = false; - arg.pos.x = 0; - arg.pos.y = 0; - arg.window_handle = reinterpret_cast(msgwnd); + msgwnd->flags.action = mouse_action::normal; - emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); - brock.wd_manager().do_lazy_refresh(msgwnd, false); + arg_click click_arg; + click_arg.mouse_args = nullptr; + click_arg.window_handle = reinterpret_cast(msgwnd); + + auto retain = msgwnd->together.events_ptr; + if (brock.emit(event_code::click, msgwnd, click_arg, true, &context)) + { + arg_mouse arg; + arg.alt = false; + arg.button = ::nana::mouse::left_button; + arg.ctrl = false; + arg.evt_code = event_code::mouse_up; + arg.left_button = true; + arg.mid_button = false; + arg.pos.x = 0; + arg.pos.y = 0; + arg.window_handle = reinterpret_cast(msgwnd); + + emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); + brock.wd_manager().do_lazy_refresh(msgwnd, false); + } + pressed_wd_space = nullptr; + } + else + { + arg_keyboard arg; + arg.evt_code = event_code::key_release; + arg.window_handle = reinterpret_cast(msgwnd); + arg.key = static_cast(wParam); + brock.get_key_state(arg); + arg.ignore = false; + brock.emit(event_code::key_release, msgwnd, arg, true, &context); } - pressed_wd_space = nullptr; - } - else - { - arg_keyboard arg; - arg.evt_code = event_code::key_release; - arg.window_handle = reinterpret_cast(msgwnd); - arg.key = static_cast(wParam); - brock.get_key_state(arg); - arg.ignore = false; - brock.emit(event_code::key_release, msgwnd, arg, true, &context); } } } @@ -1625,7 +1637,7 @@ namespace detail if ((!wd) && pre && (pre->root != get_menu())) { internal_scope_guard lock; - wd_manager().set_focus(pre, false); + wd_manager().set_focus(pre, false, arg_focus::reason::general); wd_manager().update(pre, true, false); } } diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 4aa19757..fcb7fe6e 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -925,7 +925,7 @@ namespace detail //set_focus //@brief: set a keyboard focus to a window. this may fire a focus event. - window_manager::core_window_t* window_manager::set_focus(core_window_t* wd, bool root_has_been_focused) + window_manager::core_window_t* window_manager::set_focus(core_window_t* wd, bool root_has_been_focused, arg_focus::reason reason) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -951,6 +951,7 @@ namespace detail arg.getting = false; arg.window_handle = reinterpret_cast(prev_focus); arg.receiver = wd->root; + arg.focus_reason = arg_focus::reason::general; brock.emit(event_code::focus, prev_focus, arg, true, brock.get_thread_context()); } @@ -968,6 +969,7 @@ namespace detail arg.window_handle = reinterpret_cast(wd); arg.getting = true; arg.receiver = wd->root; + arg.focus_reason = reason; brock.emit(event_code::focus, wd, arg, true, brock.get_thread_context()); if (!root_has_been_focused) diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index f653069c..c645de9a 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -929,7 +929,7 @@ namespace API void focus_window(window wd) { - restrict::wd_manager().set_focus(reinterpret_cast(wd), false); + restrict::wd_manager().set_focus(reinterpret_cast(wd), false, arg_focus::reason::general); restrict::wd_manager().update(reinterpret_cast(wd), false, false); } @@ -1171,7 +1171,7 @@ namespace API window move_tabstop(window wd, bool next) { basic_window* ts_wd = restrict::wd_manager().tabstop(reinterpret_cast(wd), next); - restrict::wd_manager().set_focus(ts_wd, false); + restrict::wd_manager().set_focus(ts_wd, false, arg_focus::reason::general); restrict::wd_manager().update(ts_wd, false, false); return reinterpret_cast(ts_wd); } diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index fc6086cc..3c372903 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1289,7 +1289,11 @@ namespace nana{ namespace widgets text_area_.tab_space = 4; text_area_.scroll_pixels = 16; text_area_.hscroll = text_area_.vscroll = 0; - select_.mode_selection = selection::mode_no_selected; + + select_.behavior = text_focus_behavior::select_if_tabstop_or_click; + select_.move_to_end = false; + select_.mode_selection = selection::mode::no_selected; + select_.ignore_press = false; select_.dragged = false; API::create_caret(wd, 1, line_height()); @@ -1348,11 +1352,13 @@ namespace nana{ namespace widgets void text_editor::erase_keyword(const ::std::wstring& kw) { for (auto i = keywords_->kwbase.begin(); i != keywords_->kwbase.end(); ++i) + { if (i->text == kw) { keywords_->kwbase.erase(i); return; } + } } void text_editor::set_accept(std::function pred) @@ -1624,6 +1630,47 @@ namespace nana{ namespace widgets return 0; } + bool text_editor::focus_changed(const arg_focus& arg) + { + bool renderred = false; + + if (arg.getting && (select_.a == select_.b)) //Do not change the selected text + { + bool select_all = false; + switch (select_.behavior) + { + case text_focus_behavior::select: + select_all = true; + break; + case text_focus_behavior::select_if_click: + select_all = (arg_focus::reason::mouse_press == arg.focus_reason); + break; + case text_focus_behavior::select_if_tabstop: + select_all = (arg_focus::reason::tabstop == arg.focus_reason); + break; + case text_focus_behavior::select_if_tabstop_or_click: + select_all = (arg_focus::reason::tabstop == arg.focus_reason || arg_focus::reason::mouse_press == arg.focus_reason); + default: + break; + } + + if (select_all) + { + select(true); + move_caret_end(); + renderred = true; + + //If the text widget is focused by clicking mouse button, the selected text will be cancelled + //by the subsequent mouse down event. In this situation, the subsequent mouse down event should + //be ignored. + select_.ignore_press = (arg_focus::reason::mouse_press == arg.focus_reason); + } + } + show_caret(arg.getting); + reset_caret(); + return renderred; + } + bool text_editor::mouse_enter(bool enter) { if((false == enter) && (false == text_area_.captured)) @@ -1649,7 +1696,7 @@ namespace nana{ namespace widgets auto caret_pos_before = caret(); mouse_caret(scrpos); - if(select_.mode_selection != selection::mode_no_selected) + if(select_.mode_selection != selection::mode::no_selected) set_end_caret(); else if ((!select_.dragged) && (caret_pos_before != caret())) select_.dragged = true; @@ -1664,8 +1711,11 @@ namespace nana{ namespace widgets { if (event_code::mouse_down == arg.evt_code) { - if (!hit_text_area(arg.pos)) + if (select_.ignore_press || (!hit_text_area(arg.pos))) + { + select_.ignore_press = false; return false; + } if (::nana::mouse::left_button == arg.button) { @@ -1691,7 +1741,7 @@ namespace nana{ namespace widgets } points_.shift_begin_caret = points_.caret; } - select_.mode_selection = selection::mode_mouse_selected; + select_.mode_selection = selection::mode::mouse_selected; } text_area_.border_renderer(graph_, _m_bgcolor()); @@ -1699,11 +1749,12 @@ namespace nana{ namespace widgets } else if (event_code::mouse_up == arg.evt_code) { - auto is_prev_no_selected = (select_.mode_selection == selection::mode_no_selected); + select_.ignore_press = false; + auto is_prev_no_selected = (select_.mode_selection == selection::mode::no_selected); - if (select_.mode_selection == selection::mode_mouse_selected) + if (select_.mode_selection == selection::mode::mouse_selected) { - select_.mode_selection = selection::mode_no_selected; + select_.mode_selection = selection::mode::no_selected; set_end_caret(); } else if (is_prev_no_selected) @@ -1842,12 +1893,12 @@ namespace nana{ namespace widgets select_.b.y = static_cast(textbase_.lines()); if(select_.b.y) --select_.b.y; select_.b.x = static_cast(textbase_.getline(select_.b.y).size()); - select_.mode_selection = selection::mode_method_selected; + select_.mode_selection = selection::mode::method_selected; render(true); return true; } - select_.mode_selection = selection::mode_no_selected; + select_.mode_selection = selection::mode::no_selected; if (_m_cancel_select(0)) { render(true); @@ -1924,6 +1975,16 @@ namespace nana{ namespace widgets return text_position_; } + void text_editor::focus_behavior(text_focus_behavior behavior) + { + select_.behavior = behavior; + } + + void text_editor::select_behavior(bool move_to_end) + { + select_.move_to_end = move_to_end; + } + void text_editor::draw_corner() { if(text_area_.vscroll && text_area_.hscroll) @@ -2339,27 +2400,45 @@ namespace nana{ namespace widgets size_t lnsz = textbase_.getline(caret.y).size(); switch (key) { case keyboard::os_arrow_left: - if (caret.x != 0) { - --caret.x; + if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) + { + caret = select_.a; changed = true; - }else { - if (caret.y != 0) { - --caret.y; - caret.x = static_cast(textbase_.getline(caret.y).size()); + } + else + { + if (caret.x != 0) { + --caret.x; changed = true; } + else { + if (caret.y != 0) { + --caret.y; + caret.x = static_cast(textbase_.getline(caret.y).size()); + changed = true; + } + } } break; case keyboard::os_arrow_right: - if (caret.x < lnsz) { - ++caret.x; + if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) + { + caret = select_.b; changed = true; - }else { - if (caret.y != nlines - 1) { - ++caret.y; - caret.x = 0; + } + else + { + if (caret.x < lnsz) { + ++caret.x; changed = true; } + else { + if (caret.y != nlines - 1) { + ++caret.y; + caret.x = 0; + changed = true; + } + } } break; case keyboard::os_arrow_up: @@ -2411,6 +2490,7 @@ namespace nana{ namespace widgets if (select_.a != caret || select_.b != caret) { changed = true; } + if (changed) { if (arg.shift) { switch (key) { diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index fe140e41..b6d678af 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -94,14 +94,9 @@ namespace drawerbase { void drawer::focus(graph_reference graph, const arg_focus& arg) { - refresh(graph); - if (!editor_->attr().multi_lines && arg.getting) - { - editor_->select(true); - editor_->move_caret_end(); - } - editor_->show_caret(arg.getting); - editor_->reset_caret(); + if (!editor_->focus_changed(arg)) + refresh(graph); + API::lazy_refresh(); } @@ -590,6 +585,22 @@ namespace drawerbase { return (editor ? editor->line_height() : 0); } + void textbox::focus_behavior(text_focus_behavior behavior) + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if (editor) + editor->focus_behavior(behavior); + } + + void textbox::select_behavior(bool move_to_end) + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if (editor) + editor->select_behavior(move_to_end); + } + //Override _m_caption for caption() auto textbox::_m_caption() const throw() -> native_string_type {