4

I recently installed Github for Windows on my Windows 7 machine and loved the custom frame it had, it fit really well with the overall application theme and had it's own titlebar buttons which were really well layed out, very fluent, and seemed very natural to work with.

I did a bit of digging and found 2 flags that would clear out the border completely and after a bit of customization I got my app to also have a nicely customized look which was intuitive yet different from all the apps with the old Windows border.

The thing was it wasn't fluent and naturally responsive like the other windows, it was glitchy as heck, I easily got the window to move around with the mouse but it often glitched and was able to be moved on areas it shouldn't like clicking and dragging on a disabled button.

The maximize button which was linked to showMaximize method just enlarged the whole window to take up the entire desktop, you could still move it (wasnt really trully maximized).

The window responded to none of the system signals like clicking the taskbar to minimize it and such.

After a lot of fixing around I just finally gave up which was ashame cause I really liekd how it looked and it was very intuitive, much like github for Windows is very intuitive.

Is there any way I can accomplish this, I'm really not ready to give up yet.

I know that when making a raw Windows API application you have to link it to the XP built-in style because it inherits the Windows 95 style by default, maybe theres a Windows 8 style that Qt'S not connected to, I do't know didn'T go that far in research yet.

黒い雪
  • 563
  • 9
  • 18
  • I know this is quite old, but recently I've developed a base program with custom borders (even resizeable). So, if somebody else needs here is the link: https://github.com/Nintersoft/CustomTitlebar/ – mauroaraujo Jun 12 '17 at 01:28

2 Answers2

5

Minimize window by clicking on task bar

It seems that Qt::FramelessWindowHint's implementation is limited. When this flag is set, Windows thinks that this window cannot be minimized or maximized. I've tried this solution implemented in pure winapi. Minimizing and restoring frameless window by clicking on taskbar works fine. Apparently Qt sets some bad flags that block this functionality. May be there is a good reason for that, I don't know.

We can use winapi and Qt together but it is troublesome. Firstly, winapi code should be executed after you set window flags and show the window using Qt. Otherwise Qt will overwrite window flags.

Another problem is when we remove border using winapi, window geometry suddently changes, and Qt doesn't know about that. Rendering and event mapping (including mouse click positions) become invalid. I didn't find any documented way to update mapping. I've found that we can tell Qt that screen orientation has changed, and it forces it to recalculate window geometry. But this looks like a dirty hack. Also the QWidget::windowHandle function is missing in Qt 4 and "is subject to change" in Qt 5. So this method is not reliable. But anyway, it works now. Here is complete code (tested in Windows 8) that should be placed in the top window class constructor:

#include "windows.h"
#include <QWindow>
//...
show();
HWND hwnd = reinterpret_cast<HWND>(effectiveWinId());
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
SetWindowLong(hwnd, GWL_STYLE, lStyle);
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
windowHandle()->reportContentOrientationChange(Qt::PrimaryOrientation);

The true way to solve this problem is to modify the Window Qt platform plugin (see QWindowsWindow class in Qt sources). May be there is a way to inherit from the default implementation, modify it and use in your app. Also you can ask Qt developers is this behavior reasonable or is it a bug. I think that this issue can be fixed with a patch.

If you still intend to use this code and other OSs should be also supported, don't forget to wrap windows-specific implementation in #ifdef Q_OS_WIN.

Enable window dragging only when title bar is clicked and window is not maximized

Other problems can be fixed more easily. When you process mouse events to implement window dragging, check window state and event position and disable moving when it is unwanted.

void MainWindow::mousePressEvent(QMouseEvent *e) {
  if (!isMaximized() && 
      e->button() == Qt::LeftButton && 
      ui->title->geometry().contains(e->pos())) {
    window_drag_start_pos = e->pos();
  }
}

void MainWindow::mouseReleaseEvent(QMouseEvent *e) {
  window_drag_start_pos = QPoint(0, 0);
}

void MainWindow::mouseMoveEvent(QMouseEvent *e) {
  if (!window_drag_start_pos.isNull()) {
    move(pos() + e->pos() - window_drag_start_pos);
  }
}

void MainWindow::on_minimize_clicked() {
  showMinimized();
}

void MainWindow::on_maximize_clicked() {
  if (isMaximized()) {
    showNormal();
  } else {
    showMaximized();
  }
}

Here ui->title is a label used for displaying fake title bar, and QPoint window_drag_start_pos is a class variable.

Community
  • 1
  • 1
Pavel Strakhov
  • 39,123
  • 5
  • 88
  • 127
  • The isMaximized method apparently isn't very helpful either, I tried that exact code for on_maximize_clicked(), apparently it check to see if the window is at location 0,0 and is the width and height of the screen. The window can still be moved, once moved its no longer at 0,0 so isMaximized returns false so it tries to maximize, but the size is still the monitor width and height so nothing happens. A second click apparently knocks it back to restore mode. Also I tried the title bar as well, If the title bar buttons are in the title bar then clicking and dragging on a button moves the window – 黒い雪 Oct 31 '13 at 22:26
  • I overcame this by adding an invisible grip in the title bar next to the buttons and that made it work but I never got it quite right, you had to click in the middle of the grip despite the fact I had removed all the margins and expanded the grip vertically, etc... In the end I guess its just not possible without getting too messy, I'd prefer a nice fluent app than one that looks nice and elegant but glitchy as heck – 黒い雪 Oct 31 '13 at 22:29
  • I don't experience described problems. `on_maximize_clicked` code and `isMaimized` work perfectly for me. `ui->title->geometry()` doesn't contain position of maximize/minimize buttons, so moving window by clicking on these buttons is just impossible. May be these problems are caused by some issues in your code. Try to run my [full code](https://gist.github.com/Riateche/7258849). – Pavel Strakhov Oct 31 '13 at 23:27
  • 1
    Regarding minimizing: if you set `Qt::WindowMinimizeButtonHint` along with `Qt::FramelessWindowHint` (i.e. `setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);`), then the window will *not* have a minimize button but can still be minimized by clicking on the taskbar icon. – Ignitor Feb 01 '14 at 20:04
  • Qt::FramelessWindowHint is not an optimal solution. You lose all Windows Aero specific functions this way, and maximizing the window does not work (instead, it goes into fullscreen, hiding the taskbar). – i know nothing Mar 01 '17 at 08:26
1

If you use Qt::FramelessWindowHint, you lose all Windows frame related features, such as docking, shortcuts, maximizing, etc. You can implement some of them yourself, with great effort in some cases, but other things will still just not work, including handling multiple monitors and all the capabilities of the Windows Key. If you need your app to behave like a regular Windows app, Qt::FramelessWindowHint is pretty much a dead end.

The only real solution is to use the DWM API that Windows provides for this purpose, Custom Window Frame Using DWM. They provide examples of how to do anything you might want in the frame area, while preserving all the standard windows-manager behaviors.

Applications such as Chrome do use DWM for this (see the AeroGlassFrame class in the Chromium source). Granted, Chrome isn't using Qt, so it doesn't have to do the work of sneaking around QMainWindow to handle the Windows-specific messages, and their code has plenty of comments that indicate how messy it is, like:

 // Hack necessary to stop black background flicker, we cut out
 // resizeborder here to save us from having to do too much
 // addition and subtraction in Layout() ...

Similarly, the Github-for-Windows app you mention is built on a platform called Electron, which in turn uses, you guessed it, DWM.

I haven't found any working example of a DWM frame around a Qt application, but hopefully this provides a starting point.

bdiscoe
  • 41
  • 1
  • 5