diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index 4153d439..a582d7b9 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -51,6 +51,13 @@ namespace nana top_left, top_right, bottom_left, bottom_right }; + enum class window_relationship + { + owner, ///< Owner window. + parent, ///< Parent window. + either_po ///< One between owner and parent. + }; + enum class bground_mode { none, diff --git a/include/nana/gui/detail/native_window_interface.hpp b/include/nana/gui/detail/native_window_interface.hpp index 25b2fc7f..e2a997c6 100644 --- a/include/nana/gui/detail/native_window_interface.hpp +++ b/include/nana/gui/detail/native_window_interface.hpp @@ -1,7 +1,7 @@ /* * Platform Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -76,8 +76,8 @@ namespace detail static native_string_type window_caption(native_window_type); static void capture_window(native_window_type, bool); static nana::point cursor_position(); - static native_window_type get_owner_window(native_window_type); - static native_window_type parent_window(native_window_type); + + static native_window_type get_window(native_window_type wd, window_relationship); static native_window_type parent_window(native_window_type child, native_window_type new_parent, bool returns_previous); //For Caret static void caret_create(native_window_type, const ::nana::size&); diff --git a/include/nana/gui/widgets/widget.hpp b/include/nana/gui/widgets/widget.hpp index d7be3acf..7448eea9 100644 --- a/include/nana/gui/widgets/widget.hpp +++ b/include/nana/gui/widgets/widget.hpp @@ -170,7 +170,8 @@ namespace nana } /// Base class of all the classes defined as a widget window. Defaultly a widget_tag - template + template::value>::type> //type DrawerTrigger must be derived from nana::drawer_trigger class widget_object: public detail::widget_base { protected: @@ -182,7 +183,9 @@ namespace nana widget_object() : events_{ std::make_shared() }, scheme_{ API::dev::make_scheme() } - {} + { + static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger"); + } ~widget_object() { @@ -291,7 +294,7 @@ namespace nana };//end class widget_object /// Base class of all the classes defined as a non-graphics-buffer widget window. The second template parameter DrawerTrigger is always ignored.\see nana::panel - template + template //type DrawerTrigger must be derived from nana::drawer_trigger class widget_object: public detail::widget_base { protected: @@ -302,7 +305,9 @@ namespace nana widget_object() : events_{ std::make_shared() }, scheme_{ API::dev::make_scheme() } - {} + { + static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger"); + } ~widget_object() { @@ -355,7 +360,7 @@ namespace nana /// Base class of all the classes defined as a root window. \see nana::form - template + template //type DrawerTrigger must be derived from nana::drawer_trigger class widget_object: public detail::widget_base { protected: @@ -367,10 +372,12 @@ namespace nana widget_object() : widget_object(nullptr, false, API::make_center(300, 150), appearance(), this) { + static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger"); } widget_object(window owner, bool nested, const rectangle& r = {}, const appearance& apr = {}) { + static_assert(std::is_base_of<::nana::drawer_trigger, DrawerTrigger>::value, "The type DrawerTrigger must be derived from nana::drawer_trigger"); handle_ = API::dev::create_window(owner, nested, r, apr, this); _m_bind_and_attach(); } diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index da46d6ea..1a58ec6c 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -486,6 +486,7 @@ namespace detail atombase_.wm_protocols = ::XInternAtom(display_, "WM_PROTOCOLS", False); atombase_.wm_change_state = ::XInternAtom(display_, "WM_CHANGE_STATE", False); atombase_.wm_delete_window = ::XInternAtom(display_, "WM_DELETE_WINDOW", False); + atombase_.net_frame_extents = ::XInternAtom(display_, "_NET_FRAME_EXTENTS", False); atombase_.net_wm_state = ::XInternAtom(display_, "_NET_WM_STATE", False); atombase_.net_wm_state_skip_taskbar = ::XInternAtom(display_, "_NET_WM_STATE_SKIP_TASKBAR", False); atombase_.net_wm_state_fullscreen = ::XInternAtom(display_, "_NET_WM_STATE_FULLSCREEN", False); diff --git a/source/detail/posix/platform_spec.hpp b/source/detail/posix/platform_spec.hpp index 167295bb..a4755370 100644 --- a/source/detail/posix/platform_spec.hpp +++ b/source/detail/posix/platform_spec.hpp @@ -132,6 +132,7 @@ namespace detail Atom wm_change_state; Atom wm_delete_window; //ext + Atom net_frame_extents; Atom net_wm_state; Atom net_wm_state_skip_taskbar; Atom net_wm_state_fullscreen; diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index a69d5807..221d04c8 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -308,11 +308,11 @@ namespace nana { if (pi_data_->menu.window && (pi_data_->menu.window != wd)) { - wd = native_interface::get_owner_window(wd); + wd = native_interface::get_window(wd, window_relationship::owner); while (wd) { if (wd != pi_data_->menu.window) - wd = native_interface::get_owner_window(wd); + wd = native_interface::get_window(wd, window_relationship::owner); else return false; } @@ -329,7 +329,7 @@ namespace nana erase_menu(true); pi_data_->menu.window = menu_wd; - pi_data_->menu.owner = native_interface::get_owner_window(menu_wd); + pi_data_->menu.owner = native_interface::get_window(menu_wd, window_relationship::owner); pi_data_->menu.has_keyboard = has_keyboard; } } diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index abb38f11..62dd68ee 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -26,6 +26,9 @@ namespace nana { namespace detail { + //Declarations of helper functions defined in native_window_interface.cpp + void x11_apply_exposed_position(native_window_type wd); + #pragma pack(1) union event_mask { @@ -905,6 +908,9 @@ namespace detail break; case MapNotify: case UnmapNotify: + if(xevent.type == MapNotify) + x11_apply_exposed_position(native_window); + brock.event_expose(msgwnd, (xevent.type == MapNotify)); context.platform.motion_window = nullptr; break; @@ -1281,7 +1287,7 @@ namespace detail if(condition_wd && is_modal) { native_window_type modal = reinterpret_cast(condition_wd)->root; - owner_native = native_interface::get_owner_window(modal); + owner_native = native_interface::get_window(modal, window_relationship::owner); if(owner_native) { native_interface::enable_window(owner_native, false); diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index ed1e5772..1f683075 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -1,7 +1,7 @@ /* * Platform Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -129,6 +129,140 @@ namespace nana{ { nana::detail::platform_spec & spec = nana::detail::platform_spec::instance(); } + + struct frame_extents + { + long left; + long right; + long top; + long bottom; + }; + + frame_extents x11_frame_extents(Window wd) + { + frame_extents fm_extents; + Atom type; + int format; + unsigned long len, bytes_left = 0; + unsigned char *data; + + if(Success == ::XGetWindowProperty(restrict::spec.open_display(), wd, + restrict::spec.atombase().net_frame_extents, 0, 16, 0, + XA_CARDINAL, &type, &format, + &len, &bytes_left, &data)) + { + if(type != None && len == 4) + { + fm_extents.left = ((long*)data)[0]; + fm_extents.right = ((long*)data)[1]; + fm_extents.top = ((long*)data)[2]; + fm_extents.bottom = ((long*)data)[3]; + } + ::XFree(data); + } + + return fm_extents; + } + + //The XMoveWindow and XMoveResizeWindow don't take effect if the specified window is + //unmapped, and the members x and y of XSetSizeHints is obsoluted. So the position that + //set to a unmapped windows should be kept and use the position when the window is mapped. + std::map exposed_positions; //locked by platform_scope_guard + + //Returns the parent window. + //It may return a decoration frame window if the requested window is a top level and WM is a + //reparenting window manager. + native_window_type x11_parent_window(native_window_type wd) + { + Window root; + Window parent; + Window * children; + unsigned size; + + platform_scope_guard lock; + + if(0 != ::XQueryTree(restrict::spec.open_display(), reinterpret_cast(wd), + &root, &parent, &children, &size)) + { + ::XFree(children); + return reinterpret_cast(parent); + } + return nullptr; + } + + // Revise the position for moving window. Because the window is moved depending on + //implementation of Window Manager. A reparenting window may not be moved the origin to + //the specified location. it may be moved the left-top corner to the specified location. + void x11_revise_position(native_window_type wd, int &x, int& y) + { + auto const disp = restrict::spec.open_display(); + auto const owner = reinterpret_cast(restrict::spec.get_owner(wd)); + auto const root_wd = restrict::spec.root_window(); + + Window decoration_wd = 0; + if(owner) + { + Window child; + + if(owner != root_wd) + { + ::XTranslateCoordinates(disp, owner, root_wd, + x, y, &x, &y, &child); + } + + decoration_wd = reinterpret_cast(x11_parent_window(wd)); + if((decoration_wd == owner) || (decoration_wd == root_wd)) + decoration_wd = 0; + } + + if(decoration_wd) + { + auto fm_extents = x11_frame_extents(reinterpret_cast(wd)); + + XWindowAttributes attr; + ::XGetWindowAttributes(disp, reinterpret_cast(wd), &attr); + + x += attr.x - fm_extents.left; + y += attr.y - fm_extents.top; + } + } + + void x11_apply_exposed_position(native_window_type wd) + { + nana::detail::platform_scope_guard lock; + + auto i = exposed_positions.find(reinterpret_cast(wd)); + if(i == exposed_positions.cend()) + return; + + native_interface::move_window(wd, i->second.x, i->second.y); + + exposed_positions.erase(i); + } + + namespace x11_wait + { + static Bool configure(Display *disp, XEvent *evt, char *arg) + { + return disp && evt && arg && (evt->type == ConfigureNotify) && (evt->xconfigure.window == *reinterpret_cast(arg)); + } + + static Bool map(Display *disp, XEvent *evt, char *arg) + { + return disp && evt && arg && (evt->type == MapNotify) && (evt->xmap.window == *reinterpret_cast(arg)); + } + + static Bool unmap(Display *disp, XEvent *evt, char *arg) + { + return disp && evt && arg && (evt->type == MapNotify) && (evt->xunmap.window == *reinterpret_cast(arg)); + } + } + + static void x11_wait_for(Window wd, Bool(*pred_fn)(Display*, XEvent*, char*)) + { + XEvent dummy; + ::XPeekIfEvent(restrict::spec.open_display(), &dummy, pred_fn, reinterpret_cast(&wd)); + } #endif //struct native_interface @@ -196,13 +330,6 @@ namespace nana{ return rectangle{ primary_monitor_size() }; } -#ifdef NANA_X11 - //The XMoveWindow and XMoveResizeWindow don't take effect if the specified window is - //unmapped, and the members x and y of XSetSizeHints is obsoluted. So the position that - //set to a unmapped windows should be kept and use the position when the window is mapped. - std::map exposed_positions; //locked by platform_scope_guard -#endif - //platform-dependent native_interface::window_result native_interface::create_window(native_window_type owner, bool nested, const rectangle& r, const appearance& app) { @@ -267,7 +394,7 @@ namespace nana{ XSetWindowAttributes win_attr; unsigned long attr_mask = CWBackPixmap | CWBackPixel | CWBorderPixel | - CWWinGravity | CWBitGravity | CWColormap | CWEventMask; + CWColormap | CWEventMask; Display * disp = restrict::spec.open_display(); win_attr.colormap = restrict::spec.colormap(); @@ -276,8 +403,6 @@ namespace nana{ win_attr.background_pixel = 0xFFFFFF; win_attr.border_pixmap = None; win_attr.border_pixel = 0x0; - win_attr.bit_gravity = 0; - win_attr.win_gravity = NorthWestGravity; win_attr.backing_store = 0; win_attr.backing_planes = 0; win_attr.backing_pixel = 0; @@ -424,7 +549,7 @@ namespace nana{ XSetWindowAttributes win_attr; unsigned long attr_mask = CWBackPixmap | CWBackPixel | CWBorderPixel | - CWWinGravity | CWBitGravity | CWColormap | CWEventMask; + CWColormap | CWEventMask | CWOverrideRedirect; Display * disp = restrict::spec.open_display(); win_attr.colormap = restrict::spec.colormap(); @@ -433,15 +558,12 @@ namespace nana{ win_attr.background_pixel = 0xFFFFFF; win_attr.border_pixmap = None; win_attr.border_pixel = 0x0; - win_attr.bit_gravity = 0; - win_attr.win_gravity = NorthWestGravity; win_attr.backing_store = 0; win_attr.backing_planes = 0; win_attr.backing_pixel = 0; win_attr.colormap = restrict::spec.colormap(); win_attr.override_redirect = True; - attr_mask |= CWOverrideRedirect; nana::point pos(r.x, r.y); win_attr.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | KeyPressMask | KeyReleaseMask | ExposureMask | StructureNotifyMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask; @@ -671,21 +793,21 @@ namespace nana{ { ::XMapWindow(disp, reinterpret_cast(wd)); - auto i = exposed_positions.find(reinterpret_cast(wd)); - if(i != exposed_positions.end()) - { - ::XMoveWindow(disp, reinterpret_cast(wd), i->second.x, i->second.y); - exposed_positions.erase(i); - } + //Wait for the mapping notify to update the local attribute of visibility so that + //the followed window_visible() call can return the updated visibility value. + x11_wait_for(reinterpret_cast(wd), x11_wait::map); Window grab = restrict::spec.grab(0); if(grab == reinterpret_cast(wd)) capture_window(wd, true); } else + { ::XUnmapWindow(disp, reinterpret_cast(wd)); - - ::XFlush(disp); + //Wait for the mapping notify to update the local attribute of visibility so that + //the followed window_visible() call can return the updated visibility value. + x11_wait_for(reinterpret_cast(wd), x11_wait::unmap); + } } static_cast(active); //eliminate unused parameter compiler warning. #endif @@ -847,19 +969,24 @@ namespace nana{ } return nana::point(r.left, r.top); #elif defined(NANA_X11) - int x, y; - nana::detail::platform_scope_guard psg; - Window coord_wd = reinterpret_cast(restrict::spec.get_owner(wd)); - if(!coord_wd) + point scr_pos; + nana::detail::platform_scope_guard lock; + + auto coord_wd = restrict::spec.get_owner(wd); + if(coord_wd) { - coord_wd = reinterpret_cast(parent_window(wd)); - if(!coord_wd) - coord_wd = restrict::spec.root_window(); + //wd is a top level window. It returns the position of its decoration window. + auto decr = x11_parent_window(wd); + if(decr != coord_wd) + wd = decr; } + else + coord_wd = get_window(wd, window_relationship::parent); + Window child; - if(True == ::XTranslateCoordinates(restrict::spec.open_display(), reinterpret_cast(wd), coord_wd, 0, 0, &x, &y, &child)) - return nana::point(x, y); - return nana::point(0, 0); + ::XTranslateCoordinates(restrict::spec.open_display(), reinterpret_cast(wd), reinterpret_cast(coord_wd), 0, 0, &scr_pos.x, &scr_pos.y, &child); + + return scr_pos; #endif } @@ -890,21 +1017,21 @@ namespace nana{ #elif defined(NANA_X11) Display * disp = restrict::spec.open_display(); - nana::detail::platform_scope_guard psg; - Window owner = reinterpret_cast(restrict::spec.get_owner(wd)); - if(owner) - { - Window child; - ::XTranslateCoordinates(disp, owner, restrict::spec.root_window(), - x, y, &x, &y, &child); - } + nana::detail::platform_scope_guard lock; XWindowAttributes attr; ::XGetWindowAttributes(disp, reinterpret_cast(wd), &attr); if(attr.map_state == IsUnmapped) exposed_positions[reinterpret_cast(wd)] = ::nana::point{x, y}; + + x11_revise_position(wd, x, y); + ::XMoveWindow(disp, reinterpret_cast(wd), x, y); + + //Wait for the configuration notify to update the local attribute of position so that + //the followed window_position() call can return the updated position value. + x11_wait_for(reinterpret_cast(wd), x11_wait::configure); #endif } @@ -954,16 +1081,6 @@ namespace nana{ else hints.flags = 0; - Window owner = reinterpret_cast(restrict::spec.get_owner(wd)); - int x = r.x; - int y = r.y; - if(owner) - { - Window child; - ::XTranslateCoordinates(disp, owner, restrict::spec.root_window(), - r.x, r.y, &x, &y, &child); - } - XWindowAttributes attr; ::XGetWindowAttributes(disp, reinterpret_cast(wd), &attr); if(attr.map_state == IsUnmapped) @@ -972,13 +1089,24 @@ namespace nana{ hints.width = r.width; hints.height = r.height; - exposed_positions[reinterpret_cast(wd)] = point{x, y}; + exposed_positions[reinterpret_cast(wd)] = r.position(); } if(hints.flags) ::XSetWMNormalHints(disp, reinterpret_cast(wd), &hints); + int x = r.x; + int y = r.y; + x11_revise_position(wd, x, y); ::XMoveResizeWindow(disp, reinterpret_cast(wd), x, y, r.width, r.height); + + //Wait for the configuration notify to update the local attribute of position so that + //the followed window_position() call can return the updated position value. + + //It seems that XMoveResizeWindow doesn't need x11_wait_for. But x11_wait_for is still called + //to make sure the local attribute is updated. + x11_wait_for(reinterpret_cast(wd), x11_wait::configure); + return true; #endif } @@ -1096,6 +1224,10 @@ namespace nana{ ::XSetWMNormalHints(disp, reinterpret_cast(wd), &hints); } ::XResizeWindow(disp, reinterpret_cast(wd), sz.width, sz.height); + + //It seems that XResizeWindow doesn't need x11_wait_for. But x11_wait_for is still called + //to make sure the local attribute is updated. + x11_wait_for(reinterpret_cast(wd), x11_wait::configure); return true; #endif } @@ -1231,34 +1363,29 @@ namespace nana{ #endif } - native_window_type native_interface::get_owner_window(native_window_type wd) - { -#if defined(NANA_WINDOWS) - return reinterpret_cast(::GetWindow(reinterpret_cast(wd), GW_OWNER)); -#elif defined(NANA_X11) - return restrict::spec.get_owner(wd); -#endif - } - - native_window_type native_interface::parent_window(native_window_type wd) + native_window_type native_interface::get_window(native_window_type wd, window_relationship rsp) { #ifdef NANA_WINDOWS - return reinterpret_cast(::GetParent(reinterpret_cast(wd))); + if(window_relationship::either_po == rsp) + return reinterpret_cast(::GetParent(reinterpret_cast(wd))); + else if(window_relationship::parent == rsp) + return reinterpret_cast(::GetAncestor(reinterpret_cast(wd), GA_PARENT)); + else if(window_relationship::owner == rsp) + return reinterpret_cast(::GetWindow(reinterpret_cast(wd), GW_OWNER)); #elif defined(NANA_X11) - Window root; - Window parent; - Window * children; - unsigned size; - platform_scope_guard lock; - if(0 != ::XQueryTree(restrict::spec.open_display(), reinterpret_cast(wd), - &root, &parent, &children, &size)) + auto owner = restrict::spec.get_owner(wd); + + if(window_relationship::either_po == rsp) { - ::XFree(children); - return reinterpret_cast(parent); + if(owner) + return owner; } - return nullptr; + else if(window_relationship::owner == rsp) + return owner; + + return x11_parent_window(wd); #endif } @@ -1279,7 +1406,7 @@ namespace nana{ platform_scope_guard lock; if(returns_previous) - prev = parent_window(child); + prev = get_window(child, window_relationship::either_po); if(native_window_type{} == new_parent) new_parent = reinterpret_cast(restrict::spec.root_window()); diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 374b4250..09b1e99c 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -669,7 +669,7 @@ namespace API internal_scope_guard lock; if(restrict::wd_manager().available(iwd) && (iwd->other.category == category::flags::root)) { - auto owner = interface_type::get_owner_window(iwd->root); + auto owner = interface_type::get_window(iwd->root, window_relationship::owner); if(owner) return reinterpret_cast(restrict::wd_manager().root(owner)); } diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index 21a9ffe0..8be557ef 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -267,7 +267,7 @@ namespace nana{ //When the group is resized, the drawing is called before moving the caption, but //the drawing of group requires the lastest position of caption for gradual rectangle. //For the requirement, a move event handler is required for listning the change of caption's position. - impl_->caption.events().move([this](const arg_move& arg){ + impl_->caption.events().move([this](const arg_move&){ if (align::left != impl_->caption_align) API::refresh_window(*this); });