slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
17.56k stars 604 forks source link

Window as a subcomponent is transparent #3117

Open almindor opened 1 year ago

almindor commented 1 year ago

fluent and material show transparent windows in linux, even with background specified. It also allows input events underneath if clicked inside the window but outside an active element (e.g. a button on that window).

The following snippet results in this:

transparent window


import { VerticalBox , Button, StandardListView, GroupBox} from "std-widgets.slint";

export component MainWindow inherits Window {

    popup := Window {
        x: 10px;
        y: 10px;
        z: 1.0;
        visible: false;
        background: #cecece;

        GroupBox {
            width: 100%;
            title: "Popup";

            VerticalLayout {
                width: 100%;
                Text {
                    text: "TEST";
                }

                Button {
                    text: "close";
                    clicked => {
                        popup.visible = false;
                    }
                }
            }
        }
    }

    VerticalBox { 
        StandardListView { 
            model: [
                { text: "first row" },
                { text: "second row" },
                { text: "third row" },
            ];
        }

        Button { 
            text: "popup";

            clicked => {
                popup.visible = true;
            }
        }
    }
}
tronical commented 1 year ago

I can reproduce this on macOS, too. I think the Window is not rendered with a platform window, but it's rendered in the item tree as a regular item. That will be transparent because the WindowItem's render() function is empty, as the background brush is taken into account only in the actual renderers (software, femto, skia, qt), and there it's taken into account in the callback to draw_contents but regardless of the provided component parameter.

Perhaps the easiest fix is to change draw_contents to extract the background for all renderers and pass it to the callback.

ogoffart commented 1 year ago

Window wasn't really meant to be used like this. It is meant to be a top level thing and not be use as a subelement.

If we change draw_contents to do the drawing, we would draw the window twice (once to clear the surface, then to do the implementation of draw_contents) This is especially important for translucent colors.

That said, we maybe can change the implementation so that we don't clear the contents when drawing in the backend. This should even simplify the renderer.

But still Window can't really be used like that since it has no decoration, no nothing, and not evn a x and y. I wonder if we should not just have an error (or a warning?) when using Window like that.

almindor commented 1 year ago

Window wasn't really meant to be used like this. It is meant to be a top level thing and not be use as a subelement.

If we change draw_contents to do the drawing, we would draw the window twice (once to clear the surface, then to do the implementation of draw_contents) This is especially important for translucent colors.

That said, we maybe can change the implementation so that we don't clear the contents when drawing in the backend. This should even simplify the renderer.

But still Window can't really be used like that since it has no decoration, no nothing, and not evn a x and y. I wonder if we should not just have an error (or a warning?) when using Window like that.

This happens with PopupWindow as well as Dialog however. Not only that but there is no good workaround, for example in case of Dialog even if I fill it with Rectangle of some color the bottom part reserved for buttons remains transparent.

Also I was unable to get any "window" that is on top of the main one to get exclusive input within it's area. Active elements in the top windows seem to work ok but spaces permit interaction with the bottom window.

Z ordering is also odd, I have also seen some strange occurrences where parts of bottom elements went and were drawn over the top window elements.

tronical commented 1 year ago

Window wasn't really meant to be used like this. It is meant to be a top level thing and not be use as a subelement.

If we change draw_contents to do the drawing, we would draw the window twice (once to clear the surface, then to do the implementation of draw_contents) This is especially important for translucent colors.

That said, we maybe can change the implementation so that we don't clear the contents when drawing in the backend. This should even simplify the renderer.

Could you elaborate on this a little? How would the background be drawn with your proposed approach?

Note that there's an additional complication: We offer the rendering notifier callback that's supposed to be invoked before rendering the scene, to produce underlay graphics. That means the background must be rendered at this point.

But still Window can't really be used like that since it has no decoration, no nothing, and not evn a x and y. I wonder if we should not just have an error (or a warning?) when using Window like that.

That sounds like a good idea to me.

tronical commented 1 year ago

Window wasn't really meant to be used like this. It is meant to be a top level thing and not be use as a subelement. If we change draw_contents to do the drawing, we would draw the window twice (once to clear the surface, then to do the implementation of draw_contents) This is especially important for translucent colors. That said, we maybe can change the implementation so that we don't clear the contents when drawing in the backend. This should even simplify the renderer. But still Window can't really be used like that since it has no decoration, no nothing, and not evn a x and y. I wonder if we should not just have an error (or a warning?) when using Window like that.

This happens with PopupWindow as well as Dialog however. Not only that but there is no good workaround, for example in case of Dialog even if I fill it with Rectangle of some color the bottom part reserved for buttons remains transparent.

Hm, that part works for me with PopupWindow, like this.

Also I was unable to get any "window" that is on top of the main one to get exclusive input within it's area. Active elements in the top windows seem to work ok but spaces permit interaction with the bottom window.

PopupWindow should give you that behavior, i.e. disallowing interaction with what's underneath (even outside the area).

almindor commented 1 year ago

Window wasn't really meant to be used like this. It is meant to be a top level thing and not be use as a subelement. If we change draw_contents to do the drawing, we would draw the window twice (once to clear the surface, then to do the implementation of draw_contents) This is especially important for translucent colors. That said, we maybe can change the implementation so that we don't clear the contents when drawing in the backend. This should even simplify the renderer. But still Window can't really be used like that since it has no decoration, no nothing, and not evn a x and y. I wonder if we should not just have an error (or a warning?) when using Window like that.

This happens with PopupWindow as well as Dialog however. Not only that but there is no good workaround, for example in case of Dialog even if I fill it with Rectangle of some color the bottom part reserved for buttons remains transparent.

Hm, that part works for me with PopupWindow, like this.

Also I was unable to get any "window" that is on top of the main one to get exclusive input within it's area. Active elements in the top windows seem to work ok but spaces permit interaction with the bottom window.

PopupWindow should give you that behavior, i.e. disallowing interaction with what's underneath (even outside the area).

This is what I get, a rect in the child portion of the dialog but transparent bottom with the standard buttons (and the button itself is slightly opaque)

transparent dialog

When using this slint code:


import { VerticalBox , Button, StandardListView, GroupBox, StandardButton} from "std-widgets.slint";

export component MainWindow inherits Window {

    popup := Dialog {
        x: 10px;
        y: 10px;
        z: 1.0;
        visible: false;

        Rectangle {
            background: green;

            GroupBox {
                title: "Popup";

                VerticalLayout {
                    width: 100%;
                    Text {
                        text: "TEST";
                    }
                }
            }
        }

        StandardButton {
            kind: ok;
            clicked => {
                popup.visible = false;
            }
        }
    }

    VerticalBox { 
        StandardListView { 
            model: [
                { text: "first row" },
                { text: "second row" },
                { text: "third row" },
            ];
        }

        Button { 
            text: "popup";

            clicked => {
                popup.visible = true;
            }
        }
    }
}
almindor commented 1 year ago

here's a playground link showing the Dialog case where you can still click the underlaying active items "through" the dialog as well as the transparent background around the standard buttons and the standard buttons themselves being semi-transparent.

tronical commented 1 year ago

Yes, Dialog does not behave like a popup :(. You need to use a PopupWindow like this instead.

I wonder what's actionable feedback on this issue.

  1. Disallow visible lowering on PopupWindow and Dialog?
  2. Make Dialog behave like a Popup when it's not a top-level?

Olivier, what do you think?

almindor commented 1 year ago

Yes, Dialog does not behave like a popup :(. You need to use a PopupWindow like this instead.

I wonder what's actionable feedback on this issue.

  1. Disallow visible lowering on PopupWindow and Dialog?
  2. Make Dialog behave like a Popup when it's not a top-level?

Olivier, what do you think?

Even with the popupwindow the "ok" button is semi-transparent which is just wrong.

Is Dialog completely broken atm? What do you mean by "top-level" here? Should I use it differently?

ogoffart commented 1 year ago

The idea behind Dialog is that it is used as the only window. So your root component (the one being exposed to native code) can be a Window or a Dialog but is not supposed to be used as children of other components. It was done to be practical to be used to span dialog from slint-viewer Maybe what we could do is to issue a warning. Although we did use Dialog in our own example: https://github.com/slint-ui/slint/blob/b775ec45af5f5bc4d175cedd31d81934d1ac9582/examples/todo/ui/todo.slint#L25-L43 (but note that there is a Rectangle with a background.)

But I think we should indeed have the Dialog and Window spawn an actual window when used as shildren. And have a .show() or something like that to show it.

Perhaps short term, we should add a background property that defaults to the style's window background, and lower it to a Rectangle element of that background

prof79 commented 9 months ago

A more elegant workaround/starting point meanwhile might be this.

SlintPad