X 窗口、GTKMM、重新父窗口、将子窗口绘制到父窗口中

问题描述 投票:0回答:1

我正在尝试重新设置子窗口 Gtk::Window 的父窗口,以便我可以模拟已弃用的 GTKMM 4 调用,将窗口定位在父窗口的中心(存在于 GTKMM 3 中)。我搜索了 XReparentWindow 帖子,唯一相关的帖子建议使用 XSetTransientForHint this,但这对我不起作用。我写的代码是:

    void MyGtk::Window_Centre_Child_On_Parent(
        Gtk::Window *parent,
        Gtk::Window *child
    )
    {
        int child_height;

        int child_width;

        int parent_height;

        int parent_width;

        int position_x;

        int position_y;


        //
        //First we must validate all parameters and terminate if any fail.
        //
        assert(parent);

        assert(child);

        //
        //Now we need to fetch the size of the child window so that we can then
        //re-parent the window at the centre of the parent window thus emulating
        //the GTKMM 3 set position call.  This has been deprecated in GTKMM 4.
        //
        Widget_Get_Size(
            child,
            child_width,
            child_height
        );

        //
        //Now we need to ensure that the above returned sensible values and we
        //terminate if not.
        //
        assert(child_height > 0);

        assert(child_width > 0);

        //
        //Now we need to fetch the size of the parent window so that we can then
        //re-parent the window at the centre of the parent window thus emulating
        //the GTKMM 3 set position call.  This has been deprecated in GTKMM 4.
        //
        Widget_Get_Size(
            parent,
            parent_width,
            parent_height
        );

        //
        //Now we need to ensure that the above returned sensible values and we
        //terminate if not.
        //
        assert(parent_height > 0);

        assert(parent_width > 0);

        //
        //Now we need to fetch the X windows ID for the child window.  This ID
        //is what is used to refer to windows within the X windows library.
        //
        auto child_window = Window_Get_X_Windows_ID(child);
        assert(child_window > 0);

        //
        //Now we need to fetch the X windows ID for the parent window.  This ID
        //is what is used to refer to windows within the X windows library.
        //
        auto parent_window = Window_Get_X_Windows_ID(parent);
        assert(parent_window > 0);

        //
        //Now we need to open the X windows display for the X server instance
        //that is running.  We could also use the DISPLAY environment variable
        //but the below is faster as it does not have to fetch the environment.
        //
        auto display = XOpenDisplay(XDisplayName(nullptr));
        assert(display);

        //
        //Now we need to calculate where to place the child window within the
        //parent window.  This depends on the size of the child as using the
        //centre of the parent can mean that the child window does not fit on
        //the display.
        //
        position_x = abs(
            std::min(
                parent_width / 2,
                (parent_width - child_width) / 2
            )
        );

        position_y = abs(
            std::min(
                parent_height / 2,
                (parent_height - child_height) / 2
            )
        );

        //
        //Now we need to ensure that the child window will be transient for the
        //parent window.
        //
        XSetTransientForHint(
            display,
            child_window,
            parent_window
        );

        //
        //Now we need to re-parent the child window to the parent window and we
        //set the (X, Y) co-ordinate such that the top left corner of the child
        //window will be in the centre of the parent window.
        //
        XReparentWindow(
            display,
            child_window,
            parent_window,
            position_x,
            position_y
        );

        //
        //Now we need to flush any changes made to the display.
        //
        XFlush(display);

        //
        //Finally, we need to close the display and thus release any resources
        //associated with it.
        //
        XCloseDisplay(display);
    }

我看到的是子窗口的所有装饰都被删除,但如果我不使用上面的代码并简单地显示对话框,装饰就会正确呈现。我非常感谢有关如何解决此问题的任何提示。

在堆栈溢出中搜索 XReparentWindow 并找到了一个可能的解决方案(请参阅链接),但对我来说不起作用。

x11 gtkmm4
1个回答
0
投票

从进一步的研究看来,提示例程的重新父级和设置瞬态总是会删除装饰。因此,为了解决这个问题,我将上面的代码更改为下面的代码。

        int child_height;

        int child_width;

        int parent_display_x;

        int parent_display_y;

        int parent_height;

        int parent_width;

        int move_window_x;

        int move_window_y;

        int position_x;

        int position_y;

        Window temp_child;

        XWindowAttributes child_window_attributes;

        XWindowAttributes parent_window_attributes;


        //
        //First we must validate all parameters and terminate if any fail.
        //
        assert(parent);

        assert(child);

        //
        //Now we need to fetch the X windows ID for the child window.  This ID
        //is what is used to refer to windows within the X windows library.
        //
        auto child_window = Window_Get_X_Windows_ID(child);
        assert(child_window > 0);

        //
        //Now we need to fetch the X windows ID for the parent window.  This ID
        //is what is used to refer to windows within the X windows library.
        //
        auto parent_window = Window_Get_X_Windows_ID(parent);
        assert(parent_window > 0);

        //
        //Now we need to open the X windows display for the X server instance
        //that is running.  We could also use the DISPLAY environment variable
        //but the below is faster as it does not have to fetch the environment.
        //
        auto display = XOpenDisplay(XDisplayName(nullptr));
        assert(display);

        //
        //Now we need to fetch the root window for the display.  This allows us
        //to fetch the parent window co-ordinates relative to this window and
        //thus have an absolute screen position.
        //
        auto root_window = XDefaultRootWindow(display);
        assert(root_window > 0);

        //
        //Now that we have the child window, we can now fetch the attributes of
        //the window and thus be able to obtain such information as the height
        //and width.
        //
        XGetWindowAttributes(
            display,
            child_window,
            &child_window_attributes
        );

        //
        //Now we need to fetch the size of the child window so that we can then
        //re-parent the window at the centre of the parent window thus emulating
        //the GTKMM 3 set position call.  This has been deprecated in GTKMM 4.
        //
        child_height = child_window_attributes.height;

        child_width = child_window_attributes.width;

        //
        //Now we need to ensure that the above returned sensible values and we
        //terminate if not.
        //
        assert(child_height > 0);

        assert(child_width > 0);

        //
        //Now that we have the parent window, we can now fetch the attributes of
        //the window and thus be able to obtain such information as the height
        //and width.
        //
        XGetWindowAttributes(
            display,
            parent_window,
            &parent_window_attributes
        );

        //
        //Now we need to fetch the size of the parent window so that we can then
        //re-parent the window at the centre of the parent window thus emulating
        //the GTKMM 3 set position call.  This has been deprecated in GTKMM 4.
        //
        parent_height = parent_window_attributes.height;

        parent_width = parent_window_attributes.width;

        //
        //Now we need to ensure that the above returned sensible values and we
        //terminate if not.
        //
        assert(parent_height > 0);

        assert(parent_width > 0);

        //
        //Now we need to calculate where to place the child window within the
        //parent window.  This depends on the size of the child as using the
        //centre of the parent can mean that the child window does not fit on
        //the display.
        //
        position_x =
            std::min(
                parent_width / 2,
                (parent_width - child_width) / 2
            );

        position_y =
            std::min(
                parent_height / 2,
                (parent_height - child_height) / 2
            );

        //
        //Now we need to ensure that the child window will be transient for the
        //parent window.
        //
        XSetTransientForHint(
            display,
            child_window,
            parent_window
        );

        //
        //Now we need to re-parent the child window to the parent window and we
        //set the (X, Y) co-ordinate such that the top left corner of the child
        //window will be in the centre of the parent window.
        //
        // XReparentWindow(
        //     display,
        //     child_window,
        //     parent_window,
        //     position_x,
        //     position_y
        // );

        //
        //Now we need to translate the co-ordinates so that we find the position
        //of the parent window with respect to the display's root window and
        //thus convert (0, 0) which is the top left of the parent window to the
        //actual screen co-ordinates.
        //
        XTranslateCoordinates(
            display,
            parent_window,
            root_window,
            0,
            0,
            &parent_display_x,
            &parent_display_y,
            &temp_child
        );

        //
        //Now we need to calculate the co-ordinates of where we wish to move the
        //child window to.
        //
        move_window_x =
            parent_display_x -
            parent_window_attributes.x +
            position_x;

        move_window_y =
            parent_display_y -
            parent_window_attributes.y +
            position_y;

        //
        //Now we must check the X co-ordinate and if it is negative (ie the child
        //width is more than the parent width) we will set it to zero.
        //
        if (move_window_x < 0)
        {
            move_window_x = 0;
        }

        //
        //Now we must check the Y co-ordinate and if it is negative (ie the child
        //height is more than the parent height) we will set it to zero.
        //
        if (move_window_y < 0)
        {
            move_window_y = 0;
        }

        //
        //Note: The above call to re-parent the child window to the parent window
        //causes the decorations on the child window to be removed and the child
        //is drawn as part of the parent.  What is required to emulate the GTKMM
        //3 function is for the window to be decorated and a separate and moveable
        //window.  So we will move the child window to the required location.  The
        //only issue is that we need to determine the position of the parent window
        //so that we can move the child relative to this.  This step is performed
        //above via the XTranslateCoordinates function.
        //
        XMoveWindow(
            display,
            child_window,
            move_window_x,
            move_window_y
        );

        //
        //Now we need to flush any changes made to the display.
        //
        XFlush(display);

        //
        //Finally, we need to close the display and thus release any resources
        //associated with it.
        //
        XCloseDisplay(display);

因此,首先获取相对于根窗口的 x 窗口坐标,执行一些计算以使窗口在父窗口上居中,然后移动窗口。不完全是我想要的,因为子窗口中心之前有闪烁(由于 gtkmm 中窗口的实现),但至少我可以模拟过时的调用。

© www.soinside.com 2019 - 2024. All rights reserved.