Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Frameless examples without acrylic #48

Closed
JulienMaille opened this issue Mar 9, 2021 · 68 comments
Closed

Frameless examples without acrylic #48

JulienMaille opened this issue Mar 9, 2021 · 68 comments

Comments

@JulienMaille
Copy link
Contributor

JulienMaille commented Mar 9, 2021

I know you are quite busy finding a solution for the acrylic background.
However I think the initial target of this repo was to get a working frameless mainwindow, and I'm kinda lost how to do this with latest code.
I had a working toy project using the code from around November 2020 I think. My problem at that time was that I couldn't draw the 1pixel frame around my QMainWindow.

So I decided to update to the latest code (and also tried some commit in between) but I end up with unmovable window, graphical glitches, and still no frame.

Do you plan to restore the QMainWindow example I had contributed to? Does it still work on your side?
Is QtAcrylicWidget in charge of drawing the coloured frame? Or is it a feature of FramelessWindowsManager?

@JulienMaille
Copy link
Contributor Author

Ok, so I took the time to inherit my QMainWindow from QtAcrylicWidget. The window is now movable, I believe this is mandatory with the current state of the repo.
Now the next thing is that the frame is not drawn everywhere, specially when there are QToolBars or QDockWidgets. I suppose the contentmargin only applies on the contentWidget. Do you have any trick in mind @wangwenx190 ?

@wangwenx190
Copy link
Owner

wangwenx190 commented Mar 10, 2021

However I think the initial target of this repo was to get a working frameless mainwindow, and I'm kinda lost how to do this with latest code.

I only changed the way of how the window blur, the frameless part is not touched except for some cleanups. But I did change the way of how to make a window frameless last year. I added a simple class called FramelessWindowsManager, it's cross-platform. If you want to remove a window's frame, just call FramelessWindowsManager::addWindow(window). And the addIgnoreObject function is called FramelessWindowsManager::addIgnoreObject(window, object). It's just an interface class, the actual frameless code is not changed.

Is QtAcrylicWidget in charge of drawing the coloured frame?

Ok, so I took the time to inherit my QMainWindow from QtAcrylicWidget. The window is now movable, I believe this is mandatory with the current state of the repo.

Well, the QtAcrylicWidget is a widget that have the Acrylic background, designed for cross-platform purpose. Originally it was not frameless, just a normal widget, but the appearance is much better when the window frame is gone, so I decided to make it frameless at the same time as well. It has the ability to draw the frame border indeed, if you look at it's header file. You can set the frame border's visibility, color and thickness.

If you only want to make a window frameless and don't need the acrylic background, it's not necessary to inherit from QtAcrylicWidget, just call FramelessWindowsManager::addWindow(window). It's very easy to draw a colored frame, just override the paintEvent of your QWidget/QMainWindow, and draw it using QPainter yourself. If you don't know how to do it, just look at qtacryliceffecthelper.cpp, see how paintWindowFrame() does. It's even easier for Qt Quick applications. Just put a transparent rectangle with a colored border on the top.

@wangwenx190
Copy link
Owner

Do you plan to restore the QMainWindow example I had contributed to?

I can add it back, if you wish me to do this.

Does it still work on your side?

Of course, as what I said above, the frameless code is not changed. If it works before, it will continue working.

Or is it a feature of FramelessWindowsManager?

FramelessWindowsManager won't do anything except making windows frameless. The window frame is draw either by qtacryliceffecthelper or the user himself.

Now the next thing is that the frame is not drawn everywhere, specially when there are QToolBars or QDockWidgets. I suppose the contentmargin only applies on the contentWidget. Do you have any trick in mind

The QtAcrylicWidget is just finished and I haven't test put some complex controls on it. But I'm busy dealing with the Acrylic background these days, so I do not have time to try this use case. Do you have any snapshots of this issue? Can you paste them here?

@wangwenx190
Copy link
Owner

I guess you don't need the Acrylic background. Then what about just use FramelessWindowsMananger to make the window frameless and draw the colored window frame manually in paintEvent? Will that help?

@JulienMaille
Copy link
Contributor Author

If you only want to make a window frameless and don't need the acrylic background, it's not necessary to inherit from QtAcrylicWidget, just call FramelessWindowsManager::addWindow(window).

This is what I did but ended up with the issues describes in my first post.

It's very easy to draw a colored frame, just override the paintEvent of your QWidget/QMainWindow, and draw it using QPainter yourself.

Not so easy to handle the frame color, maximized state, etc. Why reinvent the wheel if everything is already in QtAcrylicWidget.

@wangwenx190
Copy link
Owner

This is what I did but ended up with the issues describes in my first post.

I remember you provided a small trick to make it work with QMainWindow, you just need to use that again. I need to repeat this again: I didn't change the frameless code, I only changed the way of how the window blur. What works before should also continue to work now.

Not so easy to handle the frame color, maximized state, etc.

Yes, you'll have to handle all those things manually if you want to draw the window frame yourself. It's quite annoying indeed.

Why reinvent the wheel if everything is already in QtAcrylicWidget.

Because currently QtAcrylicWidget will draw the acrylic background unconditionally. I'm not sure whether you want it. You can disable the acrylic background by commenting the m_acrylicHelper.paintWindowBackground(&painter, event->region()); line though. However, the reason why I provide the QtAcrylicWidget class is to make it easier for the users who want a acrylic background, so I won't change the code. You can modify the code yourself to meet your own needs, of course.

@JulienMaille
Copy link
Contributor Author

JulienMaille commented Mar 10, 2021

So I restored the example and indeed it works without subclassing QtAcrylicWidget, but of course the frame is missing. My next step is to work on this.
image

@JulienMaille
Copy link
Contributor Author

JulienMaille commented Mar 10, 2021

Inheriting from QtAcrylicWidget gives interesting results (weird blur when resising) but the outline is there.
One pixel is missing at the bottom of the titlebar, and the QDockWidgets titlebars also have the frame missing
image

@wangwenx190
Copy link
Owner

the QDockWidgets titlebars also have the frame missing

QtAcrylicWidget will only draw the frame of itself. If you want all widgets have custom frame, maybe it can't be done in this way. Subclass QStyle and draw the frame in it may work.

@wangwenx190
Copy link
Owner

Inheriting from QtAcrylicWidget gives interesting results

QtAcrylicWidget is a subclass of QWidget, not QMainWindow, I remember you told me the QDockWidget won't work under normal QWidgets, you must inherit from QMainWindow to let it work.

And the snapshot you pasted here is much more complicated than the mainwindow example I removed, is it an improved version?

@wangwenx190
Copy link
Owner

One pixel is missing at the bottom of the titlebar

Yeah I can see it from your snapshot, but currently I have no clue about it.

@JulienMaille
Copy link
Contributor Author

JulienMaille commented Mar 11, 2021

Subclass QStyle and draw the frame in it may work

I'm willing to try this, but do you have an idea where this should take place?

QtAcrylicWidget is a subclass of QWidget

I changed it for a subclass of QMainWindow in my 2nd screenshot

And the snapshot you pasted here is much more complicated than the mainwindow example I removed, is it an improved version?
I think it's closer to a real application, I can commit that example on my fork if you want to take a look

There are several places where widgets seems to be drawn on top of the frame (see below) but only in frameless mode. (compare with 2nd screenshot)
image
image

@JulienMaille
Copy link
Contributor Author

It seems the frameless QMainWindow should be one pixel bigger in order to match the normal mode and avoid QDockWidget or QToolbars overlapping the frame

@wangwenx190
Copy link
Owner

do you have an idea where this should take place?

Sorry, I'm not familiar with QStyle and its usage. I just know you can customize a widget's look and feel by implementing your own style.

I changed it for a subclass of QMainWindow

OK, that makes sense.

I can commit that example on my fork if you want to take a look

Sure, I want to add it to the official examples if you allow me to do this.

There are several places where widgets seems to be drawn on top of the frame

Looks like we need to set some kind of contents margins to leave enough space for the window frame. What do you think?

@wangwenx190
Copy link
Owner

It seems the frameless QMainWindow should be one pixel bigger in order to match the normal mode and avoid QDockWidget or QToolbars overlapping the frame

Should the window be bigger or the content need to be smaller? I think the latter is easier to achieve.

@JulienMaille
Copy link
Contributor Author

I think the window should be bigger, but if you can explain how I can make the content smaller, let me know.

@wangwenx190
Copy link
Owner

I have just read QMainWindow's source code. It's inherited from QWidget as well. I'm going to figure out how to make a QWidget work with QDockWidgets.

@wangwenx190
Copy link
Owner

image

This is a simple QMainWindow demo developed by me. The green area is the menu bar, the yellow area is the status bar. From the snapshot we can see there's a large space between the widgets and the window frame. I just call setContentsMargins(50,50,50,50); in the constructor of the QMainWindow. So the solution now is clear and quite straight forward. To leave enough space for our own window frame, we just need to call setContentsMargins().

image

@wangwenx190
Copy link
Owner

if you can explain how I can make the content smaller, let me know.

The above comment is my answer.

@wangwenx190
Copy link
Owner

I have just read QMainWindow's source code. It's inherited from QWidget as well. I'm going to figure out how to make a QWidget work with QDockWidgets.

Sadly QMainWindow is using a special home-made layout to do all the things, its source code may be thousands of lines. And it's a private helper class, not accessiable from outside. Maybe this is not a good way to explore.

@wangwenx190
Copy link
Owner

A small hint: if you are using QtAcrylicWidget or QtAcrylicEffectHelper to draw the window frame, the window frame will always be drawn in device pixel, it will always be 1px no matter what the DPI is, this is also the standard behavior of Windows. But Qt will scale your margin automatically, so actually you should do something like this:

// In Qt5, devicePixelRatio() will round dpr to integer.
// From Qt6 these two functions are the same.
const qreal m = 1.0 / devicePixelRatioF();
setContentsMargins(m, m, m, m);

But the frame thickness can be changed by calling setFrameThickness(), but it's value will of course still be device pixels.

@JulienMaille
Copy link
Contributor Author

You're the real MVP!
image

@JulienMaille
Copy link
Contributor Author

You can have a look at my branch https://github.com/JulienMaille/framelesshelper/tree/qmainwindow
I have tweaked the min/max/close icons and it really looks native now!

@wangwenx190
Copy link
Owner

You can have a look at my branch https://github.com/JulienMaille/framelesshelper/tree/qmainwindow
I have tweaked the min/max/close icons and it really looks native now!

Thanks! I'll add it to the official examples of this repo.

@JulienMaille
Copy link
Contributor Author

JulienMaille commented Mar 13, 2021

Thanks! I'll add it to the official examples of this repo.

RIght now you can't, I dirty hacked QtAcrylicWidget to inherit from QMainWindow. I tried class templating, but QObject doesn't support it!

@wangwenx190
Copy link
Owner

I tried class templating, but QObject doesn't support it!

QObject doesn't support template indeed.

But QtAcrylicWidget is only a thin wrapper of QtAcrylicEffectHelper, you can write a QtAcrylicMainWindow as well. Maybe you can just copy QtAcrylicWidget and rename the class name.

@JulienMaille
Copy link
Contributor Author

I tried class templating, but QObject doesn't support it!

QObject doesn't support template indeed.

But QtAcrylicWidget is only a thin wrapper of QtAcrylicEffectHelper, you can write a QtAcrylicMainWindow as well. Maybe you can just copy QtAcrylicWidget and rename the class name.

Yes I could copy paste in a new class but that sounds dirty.

@JulienMaille
Copy link
Contributor Author

@wangwenx190 Do you want me to duplicate QtAcrylicWidget to QtAcrylicMainWindow and commit?

@wangwenx190
Copy link
Owner

@wangwenx190 Do you want me to duplicate QtAcrylicWidget to QtAcrylicMainWindow and commit?

I think it's fine, there's no better way anyway. We can improve it if we find some better way in the future.

@wangwenx190
Copy link
Owner

Once it's done, I think your QMainWindow exmaple can be merged as well.

@wangwenx190
Copy link
Owner

图片

I've just tried. It works.

@JulienMaille
Copy link
Contributor Author

Can I do this with just 2 qputenv calls?

@wangwenx190
Copy link
Owner

Can I do this with just 2 qputenv calls?

Of course. just call them as early as you can.

@JulienMaille
Copy link
Contributor Author

Looks like the showEvent() code is never executed.

@JulienMaille
Copy link
Contributor Author

also after playing with environment variable, the blur doesn't show all the weird bugs it had before (when resizing) and it shows the windows behind, not just the wallpaper. I'm not sure why I all those issues before, when looking at windows env vars, I can't find anything related

@wangwenx190
Copy link
Owner

Looks like the showEvent() code is never executed.

That sounds weird! I think it's impossible. Do you have any clue?

the blur doesn't show all the weird bugs it had before (when resizing)

Previously I was trying to switch between DWM blur and wallpaper blur dynamically, but I uploaded new code without testing. It turns out that the code is not working and is causing strange behavior. I removed these code in the next commit. Maybe this is what you observed. It's not related to the env vars indeed.

@JulienMaille
Copy link
Contributor Author

@wangwenx190 can you show (maybe try first) how I can test the acrylic blur with mainwindow example?
I tried this without success

qputenv(_flh_global::_flh_acrylic_forceDisableWallpaperBlur_flag, "True");
qputenv(_flh_global::_flh_acrylic_forceEnableOfficialMSWin10AcrylicBlur_flag, "True");

@wangwenx190
Copy link
Owner

@wangwenx190 can you show (maybe try first) how I can test the acrylic blur with mainwindow example?
I tried this without success

qputenv(_flh_global::_flh_acrylic_forceDisableWallpaperBlur_flag, "True");
qputenv(_flh_global::_flh_acrylic_forceEnableOfficialMSWin10AcrylicBlur_flag, "True");

Yes, I've just tried. It doesn't work indeed. For now I have no clue. It's weired. I'll investigate.

@JulienMaille
Copy link
Contributor Author

It did work 13days ago when you told me to try it. Must have been broken by a recent commit

@wangwenx190
Copy link
Owner

The acrylic background looks normal when using the wallpaper blur mode, however, it looks weired when using traditional DWM blur. The background is not transparent at all.

@JulienMaille
Copy link
Contributor Author

Yes, but it used to work fine, just a week or two ago!

@wangwenx190
Copy link
Owner

It may worked on your side some time ago. But from my side, it has this issue since it's first merged into master branch. I've checked the commit history within recent two weeks, it seems no critical changes were made.

@JulienMaille
Copy link
Contributor Author

Weird! Don't we need a setAttribute(Qt::WA_TranslucentBackground) somewhere for this to work?

@wangwenx190
Copy link
Owner

setAttribute(Qt::WA_TranslucentBackground)

I've just tried, it doesn't help. And it's not needed actually. We don't use it in QtAcrylicWidget either.

@wangwenx190
Copy link
Owner

I've figured out how to solve this strange issue. You need to subclass QtAcrylicMainWindow and enable the acrylic blur in its showEvent, just like what the widget demo does.

@wangwenx190
Copy link
Owner

Do you have time to fix this issue? I'm quite busy with another open source project these days, I can fix it of course, but maybe some later time.

@JulienMaille
Copy link
Contributor Author

Yes I will try to fix it

@wangwenx190
Copy link
Owner

Thanks! Then all the code in main() can be moved into its own ctor function.

@JulienMaille
Copy link
Contributor Author

JulienMaille commented Mar 31, 2021

Well, with every update, it is getting more and more complex to understand how the acrylic behaves.
Why would we need to subclass QtAcrylicMainWindow since we already subclass QMainWindow and reimplemented showEvent()

Actually I think it stopped working when you removed Utilities::setBlurEffectEnabled(windowHandle(), m_acrylicEnabled); from showEvent()
Now it's called too early in setAcrylicBlur() before m_inited == true

This will fix it:

void QtAcrylicMainWindow::showEvent(QShowEvent *event)
{
    QMainWindow::showEvent(event);
    updateContentMargin();
    if (!m_inited) {
        m_acrylicHelper.install(windowHandle());
        m_acrylicHelper.updateAcrylicBrush(tintColor());
        Utilities::setBlurEffectEnabled(windowHandle(), m_acrylicEnabled); // <---- HERE
        connect(&m_acrylicHelper, &QtAcrylicEffectHelper::needsRepaint, this, qOverload<>(&QtAcrylicMainWindow::update));
        m_inited = true;
    }
}

@JulienMaille
Copy link
Contributor Author

Now for the bonus part: the resulting exe does not lag when moving nor resizing on Win10 Build 21343 🎉

@wangwenx190
Copy link
Owner

Well, with every update, it is getting more and more complex to understand how the acrylic behaves.

I'm really sorry for it. It's becoming more and more complex indeed. Maybe I should move the Acrylic-related things to a separate repo.

Now it's called too early in setAcrylicBlur() before m_inited == true

Yes, that's the reason.

Now for the bonus part: the resulting exe does not lag when moving nor resizing on Win10 Build 21343

That's really a good news! But since we are supporting two different blur modes, are you sure you are using the real acrylic blur provided by MS?

@wangwenx190
Copy link
Owner

Now for the bonus part: the resulting exe does not lag when moving nor resizing on Win10 Build 21343

For this version, is it a insider build? Does it have an official name like "1809" or "20H1"? Is it the first version that fixes the laggying bug?

@wangwenx190
Copy link
Owner

I've pushed new code. The mainwindow issue is now solved.

@JulienMaille
Copy link
Contributor Author

That's really a good news! But since we are supporting two different blur modes, are you sure you are using the real acrylic blur provided by MS?
Yes, 100% sure

For this version, is it a insider build? Does it have an official name like "1809" or "20H1"? Is it the first version that fixes the laggying bug?
Yes too, insider build. I'll check the name tonite

@JulienMaille
Copy link
Contributor Author

The insider has no special name, sorry.
I noticed that the acrylic/homemade blur work even without those lines, do we really need them?

setAutoFillBackground(!m_acrylicEnabled);
setAttribute(Qt::WA_NoSystemBackground, m_acrylicEnabled);
setAttribute(Qt::WA_OpaquePaintEvent, m_acrylicEnabled);
setBackgroundRole(m_acrylicEnabled ? QPalette::Base : QPalette::Window);

@wangwenx190
Copy link
Owner

I'll check the name tonite

Thanks, but it's not needed now since it's a developer build.

I noticed that the acrylic/homemade blur work even without those lines, do we really need them?

The homemade blur comes from a commercial component, it has these lines, I'm not sure what's the exact usage of them, so I don't remove them for safe.

@wangwenx190
Copy link
Owner

I'm going to separate the frameless code and acrylic blur code within few days. This repo will only contain the frameless helper code.

@wangwenx190
Copy link
Owner

Hi, please check this repo: https://github.com/wangwenx190/qtacrylicmaterial

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants