fix and improve the internal handle of focus change

enhanced textbox behavior of focus change
This commit is contained in:
Jinhao
2016-02-27 02:02:29 +08:00
parent a839cf8deb
commit 569eb49a5c
14 changed files with 290 additions and 130 deletions

View File

@@ -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<window>(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<window>(msgwnd);
arg_click click_arg;
click_arg.mouse_args = nullptr;
click_arg.window_handle = reinterpret_cast<window>(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<window>(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<window>(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<window>(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<window>(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;

View File

@@ -549,6 +549,7 @@ namespace detail
arg.window_handle = reinterpret_cast<window>(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<window>(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<window>(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<window>(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<window>(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<window>(msgwnd);
arg.key = static_cast<wchar_t>(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<window>(msgwnd);
arg.key = static_cast<wchar_t>(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);
}
}

View File

@@ -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<decltype(mutex_)> lock(mutex_);
@@ -951,6 +951,7 @@ namespace detail
arg.getting = false;
arg.window_handle = reinterpret_cast<window>(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<window>(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)

View File

@@ -929,7 +929,7 @@ namespace API
void focus_window(window wd)
{
restrict::wd_manager().set_focus(reinterpret_cast<basic_window*>(wd), false);
restrict::wd_manager().set_focus(reinterpret_cast<basic_window*>(wd), false, arg_focus::reason::general);
restrict::wd_manager().update(reinterpret_cast<basic_window*>(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<basic_window*>(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<window>(ts_wd);
}

View File

@@ -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<bool(char_type)> 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<unsigned>(textbase_.lines());
if(select_.b.y) --select_.b.y;
select_.b.x = static_cast<unsigned>(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<decltype(caret.x)>(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<decltype(caret.x)>(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) {

View File

@@ -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
{