/* * A Bedrock Implementation * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2015 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/win32/bedrock.cpp * @contributors: Ariel Vina-Rodriguez */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A #endif #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E #endif typedef void (CALLBACK *win_event_proc_t)(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime); namespace nana { void notifications_window_proc(HWND wd, WPARAM wparam, LPARAM lparam); //Defined in notifier.cpp namespace detail { namespace restrict { typedef struct tagTRACKMOUSEEVENT{ unsigned long cbSize; unsigned long dwFlags; void* hwndTrack; unsigned long dwHoverTime; } TRACKMOUSEEVENT, *LPTRACKMOUSEEVENT; typedef int (__stdcall* track_mouse_event_type)(LPTRACKMOUSEEVENT); int __stdcall dummy_track_mouse_event(LPTRACKMOUSEEVENT) { return 1; } track_mouse_event_type track_mouse_event; typedef HIMC (__stdcall * imm_get_context_type)(HWND); imm_get_context_type imm_get_context; typedef BOOL (__stdcall* imm_release_context_type)(HWND, HIMC); imm_release_context_type imm_release_context; typedef BOOL (__stdcall* imm_set_composition_font_type)(HIMC, LOGFONTW*); imm_set_composition_font_type imm_set_composition_font; typedef BOOL (__stdcall* imm_set_composition_window_type)(HIMC, LPCOMPOSITIONFORM); imm_set_composition_window_type imm_set_composition_window; } #pragma pack(1) //Decoder of WPARAM and LPARAM struct wparam_button { bool left:1; bool right:1; bool shift:1; bool ctrl:1; bool middle:1; bool place_holder:3; char place_holder_c[1]; short wheel_delta; }; template struct param_mouse { wparam_button button; short x; short y; }; template<> struct param_mouse<8> { wparam_button button; char _x64_placeholder[4]; short x; short y; }; template struct param_size { unsigned long state; short width; short height; }; template<> struct param_size<8> { unsigned long state; char _x64_placeholder[4]; short width; short height; }; union parameter_decoder { struct { WPARAM wparam; LPARAM lparam; }raw_param; param_mouse mouse; param_size size; }; #pragma pack() struct bedrock::thread_context { unsigned event_pump_ref_count{0}; int window_count{0}; //The number of windows core_window_t* event_window{nullptr}; struct platform_detail_tag { nana::char_t keychar; }platform; struct cursor_tag { core_window_t * window; native_window_type native_handle; nana::cursor predef_cursor; HCURSOR handle; }cursor; thread_context() { cursor.window = nullptr; cursor.native_handle = nullptr; cursor.predef_cursor = nana::cursor::arrow; cursor.handle = nullptr; } }; struct bedrock::private_impl { typedef std::map thr_context_container; std::recursive_mutex mutex; thr_context_container thr_contexts; color_schemes schemes; element_store estore; struct cache_type { struct thread_context_cache { unsigned tid{ 0 }; thread_context *object{ nullptr }; }tcontext; }cache; struct menu_tag { core_window_t* taken_window{ nullptr }; bool delay_restore{ false }; native_window_type window{ nullptr }; native_window_type owner{ nullptr }; bool has_keyboard{false}; }menu; struct keyboard_tracking_state_tag { keyboard_tracking_state_tag() :alt(0) {} bool has_shortkey_occured = false; bool has_keyup = true; unsigned long alt : 2; }keyboard_tracking_state; }; //class bedrock defines a static object itself to implement a static singleton //here is the definition of this object bedrock bedrock::bedrock_object; static LRESULT WINAPI Bedrock_WIN32_WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); bedrock::bedrock() : pi_data_(new pi_data), impl_(new private_impl) { nana::detail::platform_spec::instance(); //to guaranty the platform_spec object is initialized before using. WNDCLASSEX wincl; wincl.hInstance = ::GetModuleHandle(0); wincl.lpszClassName = L"NanaWindowInternal"; wincl.lpfnWndProc = &Bedrock_WIN32_WindowProc; wincl.style = CS_DBLCLKS | CS_OWNDC; wincl.cbSize = sizeof(wincl); wincl.hIcon = ::LoadIcon (0, IDI_APPLICATION); wincl.hIconSm = wincl.hIcon; wincl.hCursor = ::LoadCursor (0, IDC_ARROW); wincl.lpszMenuName = 0; wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; wincl.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); ::RegisterClassEx(&wincl); restrict::track_mouse_event = (restrict::track_mouse_event_type)::GetProcAddress(::GetModuleHandleA("User32.DLL"), "TrackMouseEvent"); if(!restrict::track_mouse_event) restrict::track_mouse_event = restrict::dummy_track_mouse_event; HMODULE imm32 = ::GetModuleHandleA("Imm32.DLL"); restrict::imm_get_context = reinterpret_cast( ::GetProcAddress(imm32, "ImmGetContext")); restrict::imm_release_context = reinterpret_cast( ::GetProcAddress(imm32, "ImmReleaseContext")); restrict::imm_set_composition_font = reinterpret_cast( ::GetProcAddress(imm32, "ImmSetCompositionFontW")); restrict::imm_set_composition_window = reinterpret_cast( ::GetProcAddress(imm32, "ImmSetCompositionWindow")); } bedrock::~bedrock() { if(wd_manager().number_of_core_window()) { std::stringstream ss; ss<<"Nana.GUI detects a memory leaks in window_manager, "<(wd_manager().number_of_core_window())<<" window(s) are not uninstalled."; ::MessageBoxA(0, ss.str().c_str(), ("Nana C++ Library"), MB_OK); } delete impl_; delete pi_data_; } //inc_window //@brief: increament the number of windows int bedrock::inc_window(unsigned tid) { //impl refers to the object of private_impl, the object is created when bedrock is creating. private_impl * impl = instance().impl_; std::lock_guardmutex)> lock(impl->mutex); int & cnt = (impl->thr_contexts[tid ? tid : nana::system::this_thread_id()].window_count); return (cnt < 0 ? cnt = 1 : ++cnt); } auto bedrock::open_thread_context(unsigned tid) -> thread_context* { if(0 == tid) tid = nana::system::this_thread_id(); std::lock_guardmutex)> lock(impl_->mutex); if(impl_->cache.tcontext.tid == tid) return impl_->cache.tcontext.object; impl_->cache.tcontext.tid = tid; auto i = impl_->thr_contexts.find(tid); thread_context * context = (i == impl_->thr_contexts.end() ? &(impl_->thr_contexts[tid]) : &(i->second)); impl_->cache.tcontext.object = context; return context; } auto bedrock::get_thread_context(unsigned tid) -> thread_context * { if(0 == tid) tid = nana::system::this_thread_id(); std::lock_guardmutex)> lock(impl_->mutex); auto & cachectx = impl_->cache.tcontext; if(cachectx.tid == tid) return cachectx.object; auto i = impl_->thr_contexts.find(tid); if(i != impl_->thr_contexts.end()) { cachectx.tid = tid; return (cachectx.object = &(i->second)); } cachectx.tid = 0; return nullptr; } void bedrock::remove_thread_context(unsigned tid) { if(0 == tid) tid = nana::system::this_thread_id(); std::lock_guardmutex)> lock(impl_->mutex); if(impl_->cache.tcontext.tid == tid) { impl_->cache.tcontext.tid = 0; impl_->cache.tcontext.object = nullptr; } impl_->thr_contexts.erase(tid); } bedrock& bedrock::instance() { return bedrock_object; } void bedrock::map_thread_root_buffer(core_window_t* wd, bool forced, const rectangle* update_area) { auto stru = reinterpret_cast(::HeapAlloc(::GetProcessHeap(), 0, sizeof(detail::messages::map_thread))); if (stru) { if (FALSE == ::PostMessage(reinterpret_cast(wd->root), nana::detail::messages::map_thread_root_buffer, reinterpret_cast(wd), reinterpret_cast(stru))) ::HeapFree(::GetProcessHeap(), 0, stru); } } void interior_helper_for_menu(MSG& msg, native_window_type menu_window) { switch(msg.message) { case WM_KEYDOWN: case WM_CHAR: case WM_KEYUP: msg.hwnd = reinterpret_cast(menu_window); break; } } void bedrock::pump_event(window modal_window, bool is_modal) { const unsigned tid = ::GetCurrentThreadId(); auto context = this->open_thread_context(tid); if(0 == context->window_count) { //test if there is not a window //GetMessage may block if there is not a window remove_thread_context(); return; } ++(context->event_pump_ref_count); auto & intr_locker = wd_manager().internal_lock(); intr_locker.revert(); try { MSG msg; if(modal_window) { HWND native_handle = reinterpret_cast(reinterpret_cast(modal_window)->root); if (is_modal) { HWND owner = ::GetWindow(native_handle, GW_OWNER); if (owner && owner != ::GetDesktopWindow()) ::EnableWindow(owner, false); while (::IsWindow(native_handle)) { ::WaitMessage(); while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; if ((WM_KEYFIRST <= msg.message && msg.message <= WM_KEYLAST) || !::IsDialogMessage(native_handle, &msg)) { auto menu_wd = get_menu(reinterpret_cast(msg.hwnd), true); if (menu_wd) interior_helper_for_menu(msg, menu_wd); ::TranslateMessage(&msg); ::DispatchMessage(&msg); wd_manager().remove_trash_handle(tid); } } } } else { while (::IsWindow(native_handle)) { if (-1 != ::GetMessage(&msg, 0, 0, 0)) { auto menu_wd = get_menu(reinterpret_cast(msg.hwnd), true); if (menu_wd) interior_helper_for_menu(msg, menu_wd); ::TranslateMessage(&msg); ::DispatchMessage(&msg); } wd_manager().call_safe_place(tid); wd_manager().remove_trash_handle(tid); if (msg.message == WM_DESTROY && msg.hwnd == native_handle) break; }//end while } } else { while(context->window_count) { if(-1 != ::GetMessage(&msg, 0, 0, 0)) { auto menu_wd = get_menu(reinterpret_cast(msg.hwnd), true); if(menu_wd) interior_helper_for_menu(msg, menu_wd); ::TranslateMessage(&msg); ::DispatchMessage(&msg); } wd_manager().call_safe_place(tid); wd_manager().remove_trash_handle(tid); }//end while //Empty these rest messages, there is not a window to process these messages. while(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)); } } catch(std::exception& e) { (msgbox(modal_window, "An uncaptured std::exception during message pumping: ").icon(msgbox::icon_information) <<"\n in form: "<< API::window_caption(modal_window) <<"\n exception : "<< e.what() ).show(); internal_scope_guard lock; _m_except_handler(); intr_locker.forward(); if (0 == --(context->event_pump_ref_count)) { if ((nullptr == modal_window) || (0 == context->window_count)) remove_thread_context(); } throw; } catch(...) { (msgbox(modal_window, "An exception during message pumping!").icon(msgbox::icon_information) <<"An uncaptured non-std exception during message pumping!" ).show(); internal_scope_guard lock; _m_except_handler(); intr_locker.forward(); if(0 == --(context->event_pump_ref_count)) { if((nullptr == modal_window) || (0 == context->window_count)) remove_thread_context(); } throw; } intr_locker.forward(); if(0 == --(context->event_pump_ref_count)) { if((nullptr == modal_window) || (0 == context->window_count)) remove_thread_context(); } }//end pump_event void assign_arg(nana::arg_mouse& arg, basic_window* wd, unsigned msg, const parameter_decoder& pmdec) { arg.window_handle = reinterpret_cast(wd); bool set_key_state = true; switch (msg) { case WM_LBUTTONDOWN: arg.button = ::nana::mouse::left_button; arg.evt_code = event_code::mouse_down; break; case WM_RBUTTONDOWN: arg.button = ::nana::mouse::right_button; arg.evt_code = event_code::mouse_down; break; case WM_MBUTTONDOWN: arg.button = ::nana::mouse::middle_button; arg.evt_code = event_code::mouse_down; break; case WM_LBUTTONUP: arg.button = ::nana::mouse::left_button; arg.evt_code = event_code::mouse_up; break; case WM_RBUTTONUP: arg.button = ::nana::mouse::right_button; arg.evt_code = event_code::mouse_up; break; case WM_MBUTTONUP: arg.button = ::nana::mouse::middle_button; arg.evt_code = event_code::mouse_up; break; case WM_LBUTTONDBLCLK: arg.button = ::nana::mouse::left_button; arg.evt_code = (wd->flags.dbl_click ? event_code::dbl_click : event_code::mouse_down); break; case WM_MBUTTONDBLCLK: arg.button = ::nana::mouse::middle_button; arg.evt_code = (wd->flags.dbl_click ? event_code::dbl_click : event_code::mouse_down); break; case WM_RBUTTONDBLCLK: arg.button = ::nana::mouse::right_button; arg.evt_code = (wd->flags.dbl_click ? event_code::dbl_click : event_code::mouse_down); break; case WM_MOUSEMOVE: arg.button = ::nana::mouse::any_button; arg.evt_code = event_code::mouse_move; break; default: set_key_state = false; } if (set_key_state) { arg.pos.x = pmdec.mouse.x - wd->pos_root.x; arg.pos.y = pmdec.mouse.y - wd->pos_root.y; arg.alt = (::GetKeyState(VK_MENU) < 0); arg.shift = pmdec.mouse.button.shift; arg.ctrl = pmdec.mouse.button.ctrl; arg.left_button = pmdec.mouse.button.left; arg.mid_button = pmdec.mouse.button.middle; arg.right_button = pmdec.mouse.button.right; } } void assign_arg(arg_focus& arg, basic_window* wd, native_window_type recv, bool getting) { arg.window_handle = reinterpret_cast(wd); arg.receiver = recv; arg.getting = getting; } void assign_arg(arg_wheel& arg, basic_window* wd, const parameter_decoder& pmdec) { arg.window_handle = reinterpret_cast(wd); arg.evt_code = event_code::mouse_wheel; POINT point = { pmdec.mouse.x, pmdec.mouse.y }; ::ScreenToClient(reinterpret_cast(wd->root), &point); arg.upwards = (pmdec.mouse.button.wheel_delta >= 0); arg.distance = static_cast(arg.upwards ? pmdec.mouse.button.wheel_delta : -pmdec.mouse.button.wheel_delta); arg.pos.x = static_cast(point.x) - wd->pos_root.x; arg.pos.y = static_cast(point.y) - wd->pos_root.y; arg.left_button = pmdec.mouse.button.left; arg.mid_button = pmdec.mouse.button.middle; arg.right_button = pmdec.mouse.button.right; arg.ctrl = pmdec.mouse.button.ctrl; arg.shift = pmdec.mouse.button.shift; } //trivial_message // The Windows messaging always sends a message to the window thread queue when the calling is in other thread. //If messages can be finished without expecting Nana's window manager, the trivail_message function would //handle those messages. This is a method to avoid a deadlock, that calling waits for the handling and they require //Nana's window manager. bool trivial_message(HWND wd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT & ret) { bedrock & bedrock = bedrock::instance(); switch(msg) { case nana::detail::messages::async_activate: ::EnableWindow(wd, true); ::SetActiveWindow(wd); return true; case nana::detail::messages::async_set_focus: ::SetFocus(wd); return true; case nana::detail::messages::operate_caret: //Refer to basis.hpp for this specification. switch(wParam) { case 1: //Delete ::DestroyCaret(); break; case 2: //SetPos ::SetCaretPos(reinterpret_cast(lParam)->x, reinterpret_cast(lParam)->y); delete reinterpret_cast(lParam); break; } return true; case nana::detail::messages::map_thread_root_buffer: { auto stru = reinterpret_cast(lParam); bedrock.wd_manager().map(reinterpret_cast(wParam), stru->forced, (stru->ignore_update_area ? nullptr : &stru->update_area)); ::UpdateWindow(wd); ::HeapFree(::GetProcessHeap(), 0, stru); } return true; case nana::detail::messages::remote_thread_move_window: { auto * mw = reinterpret_cast(wParam); ::RECT r; ::GetWindowRect(wd, &r); if(mw->ignore & mw->Pos) { mw->x = r.left; mw->y = r.top; } else { HWND owner = ::GetWindow(wd, GW_OWNER); if(owner) { ::RECT owr; ::GetWindowRect(owner, &owr); ::POINT pos = {owr.left, owr.top}; ::ScreenToClient(owner, &pos); mw->x += (owr.left - pos.x); mw->y += (owr.top - pos.y); } } if(mw->ignore & mw->Size) { mw->width = r.right - r.left; mw->height = r.bottom - r.top; } ::MoveWindow(wd, mw->x, mw->y, mw->width, mw->height, true); delete mw; } return true; case nana::detail::messages::remote_thread_set_window_pos: ::SetWindowPos(wd, reinterpret_cast(wParam), 0, 0, 0, 0, static_cast(lParam)); return true; case nana::detail::messages::remote_thread_set_window_text: ::SetWindowTextW(wd, reinterpret_cast(wParam)); delete [] reinterpret_cast(wParam); return true; case nana::detail::messages::remote_thread_destroy_window: detail::native_interface::close_window(reinterpret_cast(wd)); //The owner would be actived before the message has posted in current thread. { internal_scope_guard sg; auto * thrd = bedrock.get_thread_context(); if(thrd && (thrd->window_count == 0)) ::PostQuitMessage(0); } ret = ::DefWindowProc(wd, msg, wParam, lParam); return true; case nana::detail::messages::tray: notifications_window_proc(wd, wParam, lParam); return true; default: break; } switch(msg) { case WM_DESTROY: case WM_SHOWWINDOW: case WM_SIZING: case WM_MOVE: case WM_SIZE: case WM_SETFOCUS: case WM_KILLFOCUS: case WM_PAINT: case WM_CLOSE: case WM_MOUSEACTIVATE: case WM_GETMINMAXINFO: case WM_WINDOWPOSCHANGED: case WM_NCDESTROY: case WM_NCLBUTTONDOWN: case WM_NCRBUTTONDOWN: case WM_NCMBUTTONDOWN: case WM_IME_STARTCOMPOSITION: case WM_DROPFILES: case WM_MOUSELEAVE: case WM_MOUSEWHEEL: //The WM_MOUSELAST may not include the WM_MOUSEWHEEL/WM_MOUSEHWHEEL when the version of SDK is low. case WM_MOUSEHWHEEL: return false; default: if((WM_MOUSEFIRST <= msg && msg <= WM_MOUSELAST) || (WM_KEYFIRST <= msg && msg <= WM_KEYLAST)) return false; } ret = ::DefWindowProc(wd, msg, wParam, lParam); return true; } void adjust_sizing(bedrock::core_window_t* wd, ::RECT * const r, int edge, unsigned req_width, unsigned req_height) { unsigned width = static_cast(r->right - r->left) - wd->extra_width; unsigned height = static_cast(r->bottom - r->top) - wd->extra_height; if(wd->max_track_size.width && (wd->max_track_size.width < req_width)) req_width = wd->max_track_size.width; else if(wd->min_track_size.width && (wd->min_track_size.width > req_width)) req_width = wd->min_track_size.width; if(wd->max_track_size.height && (wd->max_track_size.height < req_height)) req_height = wd->max_track_size.height; else if(wd->min_track_size.height && (wd->min_track_size.height > req_height)) req_height = wd->min_track_size.height; if(req_width != width) { switch(edge) { case WMSZ_LEFT: case WMSZ_BOTTOMLEFT: case WMSZ_TOPLEFT: r->left = r->right - static_cast(req_width) - wd->extra_width; break; case WMSZ_RIGHT: case WMSZ_BOTTOMRIGHT: case WMSZ_TOPRIGHT: case WMSZ_TOP: case WMSZ_BOTTOM: r->right = r->left + static_cast(req_width) + wd->extra_width; break; } } if(req_height != height) { switch(edge) { case WMSZ_TOP: case WMSZ_TOPLEFT: case WMSZ_TOPRIGHT: r->top = r->bottom - static_cast(req_height) - wd->extra_height; break; case WMSZ_BOTTOM: case WMSZ_BOTTOMLEFT: case WMSZ_BOTTOMRIGHT: case WMSZ_LEFT: case WMSZ_RIGHT: r->bottom = r->top + static_cast(req_height) + wd->extra_height; break; } } } template void emit_drawer(void (::nana::detail::drawer::*event_ptr)(const Arg&), basic_window* wd, const Arg& arg, bedrock::thread_context* thrd) { if (bedrock::instance().wd_manager().available(wd) == false) return; basic_window* prev_event_wd; if (thrd) { prev_event_wd = thrd->event_window; thrd->event_window = wd; } if (wd->other.upd_state == basic_window::update_state::none) wd->other.upd_state = basic_window::update_state::lazy; (wd->drawer.*event_ptr)(arg); if (thrd) thrd->event_window = prev_event_wd; } LRESULT CALLBACK Bedrock_WIN32_WindowProc(HWND root_window, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT window_proc_value = 0; if(trivial_message(root_window, message, wParam, lParam, window_proc_value)) return window_proc_value; static auto& brock = bedrock::instance(); static restrict::TRACKMOUSEEVENT track = {sizeof track, 0x00000002}; auto native_window = reinterpret_cast(root_window); auto* root_runtime = brock.wd_manager().root_runtime(native_window); if(root_runtime) { bool def_window_proc = false; auto& context = *brock.get_thread_context(); auto pressed_wd = root_runtime->condition.pressed; auto hovered_wd = root_runtime->condition.hovered; parameter_decoder pmdec; pmdec.raw_param.lparam = lParam; pmdec.raw_param.wparam = wParam; internal_scope_guard lock; auto msgwnd = root_runtime->window; switch(message) { case WM_IME_STARTCOMPOSITION: if(msgwnd->other.attribute.root->ime_enabled) { auto native_font = msgwnd->drawer.graphics.typeface().handle(); LOGFONTW logfont; ::GetObjectW(reinterpret_cast(native_font), sizeof logfont, &logfont); HIMC imc = restrict::imm_get_context(root_window); restrict::imm_set_composition_font(imc, &logfont); POINT pos; ::GetCaretPos(&pos); COMPOSITIONFORM cf = {CFS_POINT}; cf.ptCurrentPos = pos; restrict::imm_set_composition_window(imc, &cf); restrict::imm_release_context(root_window, imc); } def_window_proc = true; break; case WM_GETMINMAXINFO: { bool take_over = false; auto mmi = reinterpret_cast(lParam); if(msgwnd->min_track_size.width && msgwnd->min_track_size.height) { mmi->ptMinTrackSize.x = static_cast(msgwnd->min_track_size.width + msgwnd->extra_width); mmi->ptMinTrackSize.y = static_cast(msgwnd->min_track_size.height + msgwnd->extra_height); take_over = true; } if(false == msgwnd->flags.fullscreen) { if(msgwnd->max_track_size.width && msgwnd->max_track_size.height) { mmi->ptMaxTrackSize.x = static_cast(msgwnd->max_track_size.width + msgwnd->extra_width); mmi->ptMaxTrackSize.y = static_cast(msgwnd->max_track_size.height + msgwnd->extra_height); if(mmi->ptMaxSize.x > mmi->ptMaxTrackSize.x) mmi->ptMaxSize.x = mmi->ptMaxTrackSize.x; if(mmi->ptMaxSize.y > mmi->ptMaxTrackSize.y) mmi->ptMaxSize.y = mmi->ptMaxTrackSize.y; take_over = true; } } if (take_over) return 0; } break; case WM_SHOWWINDOW: if (msgwnd->visible && (wParam == FALSE)) brock.event_expose(msgwnd, false); else if ((!msgwnd->visible) && (wParam != FALSE)) brock.event_expose(msgwnd, true); def_window_proc = true; break; 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) brock.event_expose(msgwnd, false); def_window_proc = true; break; case WM_SETFOCUS: if(msgwnd->flags.enabled && msgwnd->flags.take_active) { auto focus = msgwnd->other.attribute.root->focus; if(focus && focus->together.caret) focus->together.caret->set_active(true); 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); } def_window_proc = true; break; case WM_KILLFOCUS: if(msgwnd->other.attribute.root->focus) { auto focus = msgwnd->other.attribute.root->focus; arg_focus arg; assign_arg(arg, focus, reinterpret_cast(wParam), false); if(brock.emit(event_code::focus, focus, arg, true, &context)) { if(focus->together.caret) focus->together.caret->set_active(false); } //wParam indicates a handle of window that receives the focus. brock.close_menu_if_focus_other_window(reinterpret_cast(wParam)); } 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; msgwnd = brock.wd_manager().find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); if(msgwnd && msgwnd->flags.enabled) { if (msgwnd->flags.take_active && !msgwnd->flags.ignore_mouse_focus) { auto killed = brock.wd_manager().set_focus(msgwnd, false); if (killed != msgwnd) brock.wd_manager().do_lazy_refresh(killed, false); } arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); if (brock.emit(arg.evt_code, msgwnd, arg, true, &context)) { if (brock.wd_manager().available(msgwnd)) pressed_wd = msgwnd; } } break; case WM_NCLBUTTONDOWN: case WM_NCMBUTTONDOWN: case WM_NCRBUTTONDOWN: brock.close_menu_if_focus_other_window(native_window); def_window_proc = true; break; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: msgwnd = brock.wd_manager().find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); if(nullptr == msgwnd) break; //if event on the menubar, just remove the menu if it is not associating with the menubar if ((msgwnd == msgwnd->root_widget->other.attribute.root->menubar) && brock.get_menu(msgwnd->root, true)) brock.erase_menu(true); else brock.close_menu_if_focus_other_window(msgwnd->root); if(msgwnd->flags.enabled) { pressed_wd = msgwnd; if (WM_LBUTTONDOWN == message) //Sets focus only if left button is pressed { 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); if (kill_focus != new_focus) brock.wd_manager().do_lazy_refresh(kill_focus, false); } } arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); msgwnd->flags.action = mouse_action::pressed; auto retain = msgwnd->together.events_ptr; if (brock.emit(event_code::mouse_down, msgwnd, arg, true, &context)) { //If a root_window is created during the mouse_down event, Nana.GUI will ignore the mouse_up event. if (msgwnd->root != native_interface::get_focus_window()) { auto pos = native_interface::cursor_position(); auto rootwd = native_interface::find_window(pos.x, pos.y); native_interface::calc_window_point(rootwd, pos); if(msgwnd != brock.wd_manager().find_window(rootwd, pos.x, pos.y)) { //call the drawer mouse up event for restoring the surface graphics msgwnd->flags.action = mouse_action::normal; arg.evt_code = event_code::mouse_up; emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); brock.wd_manager().do_lazy_refresh(msgwnd, false); } } } else pressed_wd = nullptr; } break; //mouse_click, mouse_up case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: msgwnd = brock.wd_manager().find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); if(nullptr == msgwnd) break; msgwnd->flags.action = mouse_action::normal; if(msgwnd->flags.enabled) { auto retain = msgwnd->together.events_ptr; ::nana::arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); ::nana::arg_click click_arg; //the window_handle of click_arg is used as a flag to determinate whether to emit click event click_arg.window_handle = nullptr; click_arg.mouse_args = &arg; if (msgwnd->dimension.is_hit(arg.pos)) { msgwnd->flags.action = mouse_action::over; if (::nana::mouse::left_button == arg.button) { click_arg.window_handle = reinterpret_cast(msgwnd); emit_drawer(&drawer::click, msgwnd, click_arg, &context); } } //Do mouse_up, this handle may be closed by click handler. if(brock.wd_manager().available(msgwnd) && msgwnd->flags.enabled) { arg.evt_code = event_code::mouse_up; emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); if (click_arg.window_handle) retain->click.emit(click_arg); if (brock.wd_manager().available(msgwnd)) { arg.evt_code = event_code::mouse_up; retain->mouse_up.emit(arg); } } else if (click_arg.window_handle) retain->click.emit(click_arg); brock.wd_manager().do_lazy_refresh(msgwnd, false); } pressed_wd = nullptr; break; case WM_MOUSEMOVE: msgwnd = brock.wd_manager().find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); if (brock.wd_manager().available(hovered_wd) && (msgwnd != hovered_wd)) { brock.event_msleave(hovered_wd); hovered_wd->flags.action = mouse_action::normal; hovered_wd = nullptr; //if msgwnd is neither captured window nor the child of captured window, //redirect the msgwnd to the captured window. auto wd = brock.wd_manager().capture_redirect(msgwnd); if(wd) msgwnd = wd; } else if(msgwnd) { bool prev_captured_inside; if(brock.wd_manager().capture_window_entered(pmdec.mouse.x, pmdec.mouse.y, prev_captured_inside)) { event_code evt_code; if(prev_captured_inside) { evt_code = event_code::mouse_leave; msgwnd->flags.action = mouse_action::normal; } else { evt_code = event_code::mouse_enter; if (pressed_wd == msgwnd) msgwnd->flags.action = mouse_action::pressed; else if (mouse_action::pressed != msgwnd->flags.action) msgwnd->flags.action = mouse_action::over; } arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); arg.evt_code = evt_code; brock.emit(evt_code, msgwnd, arg, true, &context); } } if(msgwnd) { arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); if (hovered_wd != msgwnd) { if (pressed_wd == msgwnd) msgwnd->flags.action = mouse_action::pressed; else if (mouse_action::pressed != msgwnd->flags.action) msgwnd->flags.action = mouse_action::over; hovered_wd = msgwnd; arg.evt_code = event_code::mouse_enter; brock.emit(event_code::mouse_enter, msgwnd, arg, true, &context); } arg.evt_code = event_code::mouse_move; brock.emit(event_code::mouse_move, msgwnd, arg, true, &context); track.hwndTrack = native_window; restrict::track_mouse_event(&track); } if (!brock.wd_manager().available(hovered_wd)) hovered_wd = nullptr; break; case WM_MOUSELEAVE: brock.event_msleave(hovered_wd); hovered_wd = nullptr; break; case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: { //The focus window receives the message in Windows system, it should be redirected to the hovered window ::POINT scr_pos{ pmdec.mouse.x, pmdec.mouse.y}; //Screen position auto pointer_wd = ::WindowFromPoint(scr_pos); if (pointer_wd == root_window) { ::ScreenToClient(pointer_wd, &scr_pos); auto scrolled_wd = brock.wd_manager().find_window(reinterpret_cast(pointer_wd), scr_pos.x, scr_pos.y); def_window_proc = true; auto evt_wd = scrolled_wd; while (evt_wd) { if (evt_wd->together.events_ptr->mouse_wheel.length() != 0) { def_window_proc = false; nana::point mspos{ scr_pos.x, scr_pos.y }; brock.wd_manager().calc_window_point(evt_wd, mspos); arg_wheel arg; arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical); assign_arg(arg, evt_wd, pmdec); brock.emit(event_code::mouse_wheel, evt_wd, arg, true, &context); break; } evt_wd = evt_wd->parent; } if (scrolled_wd && (nullptr == evt_wd)) { nana::point mspos{ scr_pos.x, scr_pos.y }; brock.wd_manager().calc_window_point(scrolled_wd, mspos); arg_wheel arg; arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical); assign_arg(arg, scrolled_wd, pmdec); brock.emit_drawer(event_code::mouse_wheel, scrolled_wd, arg, &context); brock.wd_manager().do_lazy_refresh(scrolled_wd, false); } } else { DWORD pid = 0; ::GetWindowThreadProcessId(pointer_wd, &pid); if (pid == ::GetCurrentProcessId()) ::PostMessage(pointer_wd, message, wParam, lParam); } } break; case WM_DROPFILES: { HDROP drop = reinterpret_cast(wParam); POINT pos; ::DragQueryPoint(drop, &pos); msgwnd = brock.wd_manager().find_window(native_window, pos.x, pos.y); if(msgwnd) { arg_dropfiles dropfiles; std::unique_ptr varbuf; std::size_t bufsize = 0; unsigned size = ::DragQueryFile(drop, 0xFFFFFFFF, 0, 0); for(unsigned i = 0; i < size; ++i) { unsigned reqlen = ::DragQueryFile(drop, i, 0, 0) + 1; if(bufsize < reqlen) { varbuf.reset(new nana::char_t[reqlen]); bufsize = reqlen; } ::DragQueryFile(drop, i, varbuf.get(), reqlen); dropfiles.files.emplace_back(utf8_cast(varbuf.get())); } while(msgwnd && (msgwnd->flags.dropable == false)) msgwnd = msgwnd->parent; if(msgwnd) { dropfiles.pos.x = pos.x; dropfiles.pos.y = pos.y; brock.wd_manager().calc_window_point(msgwnd, dropfiles.pos); dropfiles.window_handle = reinterpret_cast(msgwnd); msgwnd->together.events_ptr->mouse_dropfiles.emit(dropfiles); brock.wd_manager().do_lazy_refresh(msgwnd, false); } } ::DragFinish(drop); } break; case WM_SIZING: { ::RECT* const r = reinterpret_cast(lParam); unsigned width = static_cast(r->right - r->left) - msgwnd->extra_width; unsigned height = static_cast(r->bottom - r->top) - msgwnd->extra_height; if(msgwnd->max_track_size.width || msgwnd->min_track_size.width) { if(wParam == WMSZ_LEFT || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT) { if(msgwnd->max_track_size.width && (width > msgwnd->max_track_size.width)) r->left = r->right - static_cast(msgwnd->max_track_size.width) - msgwnd->extra_width; if(msgwnd->min_track_size.width && (width < msgwnd->min_track_size.width)) r->left = r->right - static_cast(msgwnd->min_track_size.width) - msgwnd->extra_width; } else if(wParam == WMSZ_RIGHT || wParam == WMSZ_BOTTOMRIGHT || wParam == WMSZ_TOPRIGHT) { if(msgwnd->max_track_size.width && (width > msgwnd->max_track_size.width)) r->right = r->left + static_cast(msgwnd->max_track_size.width) + msgwnd->extra_width; if(msgwnd->min_track_size.width && (width < msgwnd->min_track_size.width)) r->right = r->left + static_cast(msgwnd->min_track_size.width) + msgwnd->extra_width; } } if(msgwnd->max_track_size.height || msgwnd->min_track_size.height) { if(wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT) { if(msgwnd->max_track_size.height && (height > msgwnd->max_track_size.height)) r->top = r->bottom - static_cast(msgwnd->max_track_size.height) - msgwnd->extra_height; if(msgwnd->min_track_size.height && (height < msgwnd->min_track_size.height)) r->top = r->bottom - static_cast(msgwnd->min_track_size.height) - msgwnd->extra_height; } else if(wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT) { if(msgwnd->max_track_size.height && (height > msgwnd->max_track_size.height)) r->bottom = r->top + static_cast(msgwnd->max_track_size.height) + msgwnd->extra_height; if(msgwnd->min_track_size.height && (height < msgwnd->min_track_size.height)) r->bottom = r->top + static_cast(msgwnd->min_track_size.height) + msgwnd->extra_height; } } nana::size size_before( static_cast(r->right - r->left - msgwnd->extra_width), static_cast(r->bottom - r->top - msgwnd->extra_height)); arg_resizing arg; arg.window_handle = reinterpret_cast(msgwnd); arg.width = size_before.width; arg.height = size_before.height; switch (wParam) { case WMSZ_LEFT: arg.border = window_border::left; break; case WMSZ_RIGHT: arg.border = window_border::right; break; case WMSZ_BOTTOM: arg.border = window_border::bottom; break; case WMSZ_BOTTOMLEFT: arg.border = window_border::bottom_left; break; case WMSZ_BOTTOMRIGHT: arg.border = window_border::bottom_right; break; case WMSZ_TOP: arg.border = window_border::top; break; case WMSZ_TOPLEFT: arg.border = window_border::top_left; break; case WMSZ_TOPRIGHT: arg.border = window_border::top_right; break; } brock.emit(event_code::resizing, msgwnd, arg, false, &context); if (arg.width != width || arg.height != height) { adjust_sizing(msgwnd, r, static_cast(wParam), arg.width, arg.height); return TRUE; } } break; case WM_SIZE: if(wParam != SIZE_MINIMIZED) brock.wd_manager().size(msgwnd, size(pmdec.size.width, pmdec.size.height), true, true); break; case WM_MOVE: brock.event_move(msgwnd, (int)(short) LOWORD(lParam), (int)(short) HIWORD(lParam)); break; case WM_PAINT: { ::PAINTSTRUCT ps; ::BeginPaint(root_window, &ps); if (msgwnd->is_draw_through()) { msgwnd->other.attribute.root->draw_through(); } else { //Don't copy root_graph to the window directly, otherwise the edge nimbus effect will be missed. ::nana::rectangle update_area(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top); if (!update_area.empty()) msgwnd->drawer.map(reinterpret_cast(msgwnd), true, &update_area); } ::EndPaint(root_window, &ps); } break; case WM_SYSCHAR: def_window_proc = true; brock.set_keyboard_shortkey(true); msgwnd = brock.wd_manager().find_shortkey(native_window, static_cast(wParam)); if(msgwnd) { arg_keyboard arg; arg.evt_code = event_code::shortkey; arg.key = static_cast(wParam < 0x61 ? wParam + 0x61 - 0x41 : wParam); arg.ctrl = arg.shift = false; arg.window_handle = reinterpret_cast(msgwnd); arg.ignore = false; brock.emit(event_code::shortkey, msgwnd, arg, true, &context); def_window_proc = false; } break; case WM_SYSKEYDOWN: def_window_proc = true; if (brock.whether_keyboard_shortkey() == false) { msgwnd = msgwnd->root_widget->other.attribute.root->menubar; if (msgwnd) { bool focused = (brock.focus() == msgwnd); arg_keyboard arg; arg.evt_code = event_code::key_press; arg.window_handle = reinterpret_cast(msgwnd); arg.ignore = false; arg.key = static_cast(wParam); brock.get_key_state(arg); brock.emit(event_code::key_press, msgwnd, arg, true, &context); msgwnd->root_widget->flags.ignore_menubar_focus = (focused && (brock.focus() != msgwnd)); } else brock.erase_menu(true); } break; case WM_SYSKEYUP: def_window_proc = true; if(brock.set_keyboard_shortkey(false) == false) { msgwnd = msgwnd->root_widget->other.attribute.root->menubar; if(msgwnd) { //Don't call default window proc to avoid popuping system menu. def_window_proc = false; bool set_focus = (brock.focus() != msgwnd) && (!msgwnd->root_widget->flags.ignore_menubar_focus); if (set_focus) brock.wd_manager().set_focus(msgwnd, false); arg_keyboard arg; arg.evt_code = event_code::key_release; arg.window_handle = reinterpret_cast(msgwnd); arg.ignore = false; arg.key = static_cast(wParam); brock.get_key_state(arg); brock.emit(event_code::key_release, msgwnd, arg, true, &context); if (!set_focus) { brock.set_menubar_taken(nullptr); msgwnd->root_widget->flags.ignore_menubar_focus = false; } } } break; case WM_KEYDOWN: if(msgwnd->flags.enabled) { auto menu_wd = brock.get_menu(); if (menu_wd) brock.delay_restore(0); //Enable delay restore if (msgwnd->root != menu_wd) msgwnd = brock.focus(); if(msgwnd) { if((wParam == 9) && (!msgwnd->visible || (false == (msgwnd->flags.tab & tab_type::eating)))) //Tab { bool is_forward = (::GetKeyState(VK_SHIFT) >= 0); auto tstop_wd = brock.wd_manager().tabstop(msgwnd, is_forward); if (tstop_wd) { brock.wd_manager().set_focus(tstop_wd, false); brock.wd_manager().do_lazy_refresh(msgwnd, false); brock.wd_manager().do_lazy_refresh(tstop_wd, true); } } else { arg_keyboard arg; arg.evt_code = event_code::key_press; arg.window_handle = reinterpret_cast(msgwnd); arg.ignore = false; arg.key = static_cast(wParam); brock.get_key_state(arg); brock.emit(event_code::key_press, msgwnd, arg, true, &context); if (msgwnd->root_widget->other.attribute.root->menubar == msgwnd) { //In order to keep the focus on the menubar, cancel the delay_restore //when pressing ESC to close the menu which is popuped by the menubar. //If no menu popuped by the menubar, it should enable delay restore to //restore the focus for taken window. int cmd = (menu_wd && (keyboard::escape == static_cast(wParam)) ? 1 : 0); brock.delay_restore(cmd); } } } } break; case WM_CHAR: 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)) { arg_keyboard arg; arg.evt_code = event_code::key_char; arg.window_handle = reinterpret_cast(msgwnd); arg.key = static_cast(wParam); brock.get_key_state(arg); arg.ignore = false; 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); brock.wd_manager().do_lazy_refresh(msgwnd, false); } } return 0; case WM_KEYUP: if(wParam != 18) //MUST NOT BE AN ALT { msgwnd = brock.focus(); if(msgwnd) { 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); } } else brock.set_keyboard_shortkey(false); //Do delay restore if key is not arrow_left/right/up/down, otherwise //A menubar will be restored if the item is empty(not have a menu item) if (wParam < 37 || 40 < wParam) brock.delay_restore(2); //Restores while key release break; case WM_CLOSE: { arg_unload arg; arg.window_handle = reinterpret_cast(msgwnd); arg.cancel = false; brock.emit(event_code::unload, msgwnd, arg, true, &context); if (!arg.cancel) { def_window_proc = true; //Activate is owner, refer to the window_manager::close for the explaination if (msgwnd->flags.modal || (msgwnd->owner == 0) || msgwnd->owner->flags.take_active) native_interface::activate_owner(msgwnd->root); } break; } case WM_DESTROY: if (msgwnd->root == brock.get_menu()) { brock.erase_menu(false); brock.delay_restore(3); //Restores if delay_restore not decleared } brock.wd_manager().destroy(msgwnd); nana::detail::platform_spec::instance().release_window_icon(msgwnd->root); break; case WM_NCDESTROY: brock.manage_form_loader(msgwnd, false); brock.wd_manager().destroy_handle(msgwnd); if(--context.window_count <= 0) { ::PostQuitMessage(0); def_window_proc = true; } break; default: def_window_proc = true; } root_runtime = brock.wd_manager().root_runtime(native_window); if(root_runtime) { root_runtime->condition.pressed = pressed_wd; root_runtime->condition.hovered = hovered_wd; } if (!def_window_proc) return 0; } return ::DefWindowProc(root_window, message, wParam, lParam); } ::nana::category::flags bedrock::category(core_window_t* wd) { internal_scope_guard lock; return (wd_manager().available(wd) ? wd->other.category : ::nana::category::flags::super); } auto bedrock::focus() ->core_window_t* { core_window_t* wd = wd_manager().root(native_interface::get_focus_window()); return (wd ? wd->other.attribute.root->focus : nullptr); } void bedrock::set_menubar_taken(core_window_t* wd) { auto pre = impl_->menu.taken_window; impl_->menu.taken_window = wd; //assigning of a nullptr taken window is to restore the focus of pre taken //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); wd_manager().update(pre, true, false); } } //0:Enable delay, 1:Cancel, 2:Restores, 3: Restores when menu is destroying void bedrock::delay_restore(int state) { switch (state) { case 0: //Enable break; case 1: //Cancel break; case 2: //Restore if key released //restores the focus when menu is closed by pressing keyboard if ((!impl_->menu.window) && impl_->menu.delay_restore) set_menubar_taken(nullptr); break; case 3: //Restores if destroying //when the menu is destroying, restores the focus if delay restore is not declared if (!impl_->menu.delay_restore) set_menubar_taken(nullptr); } impl_->menu.delay_restore = (0 == state); } bool bedrock::close_menu_if_focus_other_window(native_window_type wd) { if(impl_->menu.window && (impl_->menu.window != wd)) { wd = native_interface::get_owner_window(wd); while(wd) { if(wd != impl_->menu.window) wd = native_interface::get_owner_window(wd); else return false; } erase_menu(true); return true; } return false; } void bedrock::set_menu(native_window_type menu_wd, bool has_keyboard) { if(menu_wd && impl_->menu.window != menu_wd) { erase_menu(true); impl_->menu.window = menu_wd; impl_->menu.owner = native_interface::get_owner_window(menu_wd); impl_->menu.has_keyboard = has_keyboard; } } native_window_type bedrock::get_menu(native_window_type owner, bool is_keyboard_condition) { if( (impl_->menu.owner == nullptr) || (owner && (impl_->menu.owner == owner)) ) { return ((!is_keyboard_condition) || impl_->menu.has_keyboard ? impl_->menu.window : nullptr); } return nullptr; } native_window_type bedrock::get_menu() { return impl_->menu.window; } void bedrock::erase_menu(bool try_destroy) { if (impl_->menu.window) { if (try_destroy) native_interface::close_window(impl_->menu.window); impl_->menu.window = impl_->menu.owner = nullptr; impl_->menu.has_keyboard = false; } } void bedrock::get_key_state(arg_keyboard& kb) { kb.ctrl = (0 != (::GetKeyState(VK_CONTROL) & 0x80)); kb.shift = (0 != (::GetKeyState(VK_SHIFT) & 0x80)); } bool bedrock::set_keyboard_shortkey(bool yes) { bool ret = impl_->keyboard_tracking_state.has_shortkey_occured; impl_->keyboard_tracking_state.has_shortkey_occured = yes; return ret; } bool bedrock::whether_keyboard_shortkey() const { return impl_->keyboard_tracking_state.has_shortkey_occured; } element_store& bedrock::get_element_store() const { return impl_->estore; } void bedrock::map_through_widgets(core_window_t* wd, native_drawable_type drawable) { #if defined(NANA_WINDOWS) auto graph_context = reinterpret_cast(wd->root_graph->handle()->context); for (auto child : wd->children) { if (!child->visible) continue; if (::nana::category::flags::widget == child->other.category) { ::BitBlt(reinterpret_cast(drawable), child->pos_root.x, child->pos_root.y, static_cast(child->dimension.width), static_cast(child->dimension.height), graph_context, child->pos_root.x, child->pos_root.y, SRCCOPY); } else if (::nana::category::flags::lite_widget == child->other.category) map_through_widgets(child, drawable); } #endif } bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd) { if (wd_manager().available(wd) == false) return false; basic_window* prev_event_wd; if (thrd) { prev_event_wd = thrd->event_window; thrd->event_window = wd; _m_event_filter(evt_code, wd, thrd); } if (wd->other.upd_state == core_window_t::update_state::none) wd->other.upd_state = core_window_t::update_state::lazy; _m_emit_core(evt_code, wd, false, arg); if (ask_update) wd_manager().do_lazy_refresh(wd, false); else if (wd_manager().available(wd)) wd->other.upd_state = basic_window::update_state::none; if (thrd) thrd->event_window = prev_event_wd; return true; } bool bedrock::emit_drawer(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, thread_context* thrd) { if (bedrock_object.wd_manager().available(wd) == false) return false; core_window_t* prev_event_wd; if (thrd) { prev_event_wd = thrd->event_window; thrd->event_window = wd; } if (wd->other.upd_state == core_window_t::update_state::none) wd->other.upd_state = core_window_t::update_state::lazy; _m_emit_core(evt_code, wd, true, arg); if (thrd) thrd->event_window = prev_event_wd; return true; } const nana::char_t* translate(cursor id) { const nana::char_t* name = IDC_ARROW; switch(id) { case cursor::arrow: name = IDC_ARROW; break; case cursor::wait: name = IDC_WAIT; break; case cursor::hand: name = IDC_HAND; break; case cursor::size_we: name = IDC_SIZEWE; break; case cursor::size_ns: name = IDC_SIZENS; break; case cursor::size_bottom_left: case cursor::size_top_right: name = IDC_SIZENESW; break; case cursor::size_top_left: case cursor::size_bottom_right: name = IDC_SIZENWSE; break; case cursor::iterm: name = IDC_IBEAM; break; } return name; } void bedrock::thread_context_destroy(core_window_t * wd) { auto * thr = get_thread_context(0); if (thr && thr->event_window == wd) thr->event_window = nullptr; } void bedrock::thread_context_lazy_refresh() { auto* thrd = get_thread_context(0); if (thrd && thrd->event_window) { //the state none should be tested, becuase in an event, there would be draw after an update, //if the none is not tested, the draw after update will not be refreshed. switch (thrd->event_window->other.upd_state) { case core_window_t::update_state::none: case core_window_t::update_state::lazy: thrd->event_window->other.upd_state = core_window_t::update_state::refresh; default: break; } } } //Dynamically set a cursor for a window void bedrock::set_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) { if (nullptr == thrd) thrd = get_thread_context(wd->thread_id); if ((cursor::arrow == cur) && !thrd->cursor.native_handle) return; thrd->cursor.window = wd; if ((thrd->cursor.native_handle == wd->root) && (cur == thrd->cursor.predef_cursor)) return; thrd->cursor.native_handle = wd->root; if (thrd->cursor.predef_cursor != cur) { thrd->cursor.predef_cursor = cur; thrd->cursor.handle = ::LoadCursor(nullptr, translate(cur)); } if (wd->root_widget->other.attribute.root->running_cursor != cur) { wd->root_widget->other.attribute.root->running_cursor = cur; #ifdef _WIN64 ::SetClassLongPtr(reinterpret_cast(wd->root), GCLP_HCURSOR, reinterpret_cast(thrd->cursor.handle)); #else ::SetClassLong(reinterpret_cast(wd->root), GCL_HCURSOR, static_cast(reinterpret_cast(thrd->cursor.handle))); #endif } if (cursor::arrow == thrd->cursor.predef_cursor) { thrd->cursor.window = nullptr; thrd->cursor.native_handle = nullptr; } } void bedrock::define_state_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) { wd->root_widget->other.attribute.root->state_cursor = cur; wd->root_widget->other.attribute.root->state_cursor_window = wd; set_cursor(wd, cur, thrd); auto cur_handle = ::LoadCursor(nullptr, translate(cur)); ::SetCursor(cur_handle); ::ShowCursor(TRUE); } void bedrock::undefine_state_cursor(core_window_t * wd, thread_context* thrd) { if (nullptr == thrd) thrd = get_thread_context(wd->thread_id); HCURSOR rev_handle = ::LoadCursor(nullptr, IDC_ARROW); if (!wd_manager().available(wd)) { ::ShowCursor(FALSE); ::SetCursor(rev_handle); return; } wd->root_widget->other.attribute.root->state_cursor = nana::cursor::arrow; wd->root_widget->other.attribute.root->state_cursor_window = nullptr; auto pos = native_interface::cursor_position(); auto native_handle = native_interface::find_window(pos.x, pos.y); if (!native_handle) { ::ShowCursor(FALSE); ::SetCursor(rev_handle); return; } native_interface::calc_window_point(native_handle, pos); auto rev_wd = wd_manager().find_window(native_handle, pos.x, pos.y); if (rev_wd) { set_cursor(rev_wd, rev_wd->predef_cursor, thrd); rev_handle = thrd->cursor.handle; } ::ShowCursor(FALSE); ::SetCursor(rev_handle); } void bedrock::_m_event_filter(event_code event_id, core_window_t * wd, thread_context * thrd) { auto not_state_cur = (wd->root_widget->other.attribute.root->state_cursor == nana::cursor::arrow); switch(event_id) { case event_code::mouse_enter: if (not_state_cur) set_cursor(wd, wd->predef_cursor, thrd); break; case event_code::mouse_leave: if (not_state_cur && (wd->predef_cursor != cursor::arrow)) set_cursor(wd, cursor::arrow, thrd); break; case event_code::destroy: if (wd->root_widget->other.attribute.root->state_cursor_window == wd) undefine_state_cursor(wd, thrd); if(wd == thrd->cursor.window) { set_cursor(wd, cursor::arrow, thrd); wd->root_widget->other.attribute.root->running_cursor = cursor::arrow; } break; default: break; } } }//end namespace detail }//end namespace nana