PDA

View Full Version : Unwanted window title elision



Kryzon
26th September 2015, 01:39
When I create a QDialog, the title text of the widget is elided (as in, "Supercalifragi...").

Is there a flag or some other way to request an ideal dialog size for the title text to not be elided? The contents may be expanded because of this, but this wouldn't be an issue.

Thank you.

anda_skoa
26th September 2015, 10:08
I don't think that is possible since in a lot of windowing systems drawing of window decorations is not in control of the application and the windowing system's decoration handler does not provide the feedback to react to this.

You might be able to do this in a platform specific way, e.g. using some native API to query which font is used for window titles and then use QFontMetrics to calculate the size of your title text.

Cheers,
_

Kryzon
27th September 2015, 02:42
Hello anda_skoa. I was hoping that there would be something already made, it would save a lot of time, but alas.
I did what you proposed, estimating the title bar width with the OS API.

The QDialog created, as it is. The width is given by its QLayout. If the caption text exceeds that, Windows puts an ellipsis at the end:

http://s1.postimg.org/asvnqq2hr/window_Title_Layout.png

The QDialog with a 'minimumWidth' set as the estimated title bar width. The title seems to fit:

http://s1.postimg.org/cypyl85y7/window_Title_OS.png

The main reference was this:
- http://stackoverflow.com/questions/18089280/adapt-the-size-of-a-form-to-its-title-text-in-c-sharp

But it's written in C#, so I had to use GDI32 and User32 functions.
The meat of the functionality is this:


#include <windows.h>
#include <QString>


int getRecommendedWidthWin32( const QString* titleText, int totalSystemButtons )
{
// const QString* titleText = The desired caption text for the title bar.
// int totalSystemButtons = The amount of system buttons in the title bar (i.e close button, minimise button etc.).

// Query the operating system metrics.

int finalWidth = 0;

NONCLIENTMETRICS metrics;
ZeroMemory( &metrics, sizeof( NONCLIENTMETRICS ) );
metrics.cbSize = sizeof( NONCLIENTMETRICS );

if ( SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &metrics, 0 ) != 0 )
{
// Add the width of caption buttons.
// Buttons have at least this total width or less.

finalWidth += metrics.iCaptionWidth * totalSystemButtons;

// Add the width of the frame border (left and right, so twice).
// This is commented out as it seems redundant.

//finalWidth += metrics.iBorderWidth * 2;

// Add the 'estimated' width of the caption text.

HFONT tempFont = CreateFontIndirect( &metrics.lfCaptionFont );
HDC hDC = GetDC( NULL ); // Device context for the whole screen.

SelectObject( hDC, tempFont );

SIZE textPixelSize;
GetTextExtentPoint32( hDC, reinterpret_cast< LPCWSTR >( titleText->utf16() ), titleText->size(), &textPixelSize );

ReleaseDC( NULL, hDC );
DeleteObject( tempFont );

finalWidth += textPixelSize.cx;
}

return finalWidth;
}For that dialog, the call was this:

int minimumWidth = getRecommendedWidthWin32( "Supercalifragilisticexpialidocious", 3 ) // Minimise, maximise and close buttons.

It's important to set the minimum width of your dialog to the 'qMax' between the layout sizehint and that estimated title bar width, so you use the biggest minimum width possible:

setMinimumWidth( qMax( minimumWidth, layout->sizeHint().width() ) );

anda_skoa
27th September 2015, 12:06
You could probably also use a spacer item to "add" the title width constraints to the usual layouting mechanism.

Depending on the size policy this could allow uses to make the window smaller if they wanted to, while still having the fully expanded dialog by default.

Btw, it is usually not custom to pass a QString by pointer, i.e. by value or const reference are what you will find in most cases.
Avoids either having to rely on the caller or having checks inside the callee.

Cheers,
_

Kryzon
28th September 2015, 01:13
I set it as a pointer to avoid having to #include QString in the header file, but now that you mentioned it, I realise it's unnecessary.

QObject includes the QString header (http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qobject.h#n41). So any widget code that will make use of that helper function will have it included already.
I will change it to a const reference.
Regards.

anda_skoa
28th September 2015, 07:50
Forward declaration also works if the argument is a const reference :-)

Cheers,
_

Kryzon
29th September 2015, 02:14
I did not know that. It appears that you can use references to incomplete types as members and parameters.
http://stackoverflow.com/a/553869/4206247

I suppose I can improve some unrelated code based on that.

- - - - - -

I noticed that on Windows 8.1 when you drag-resize the dialog beyond a certain point, the caption text becomes centered. In my opinion it looks more pleasant with the text centered.
So I added the following to detect if the OS version is above 8.0 and add just enough space for the caption text to be centered (it's twice the button sizes).


// Returns 'true' if this is Windows 8 and above, 'false' if not.

static bool setIsWindows8()
{
OSVERSIONINFOEX osInfoEx;
osInfoEx.dwOSVersionInfoSize = sizeof( OSVERSIONINFOEX );
GetVersionEx( reinterpret_cast< LPOSVERSIONINFOW >( &osInfoEx ) );

return ( osInfoEx.dwMajorVersion >= 6
&& ( osInfoEx.dwMinorVersion >= 2 || osInfoEx.dwMajorVersion > 6 )
&& osInfoEx.wProductType == VER_NT_WORKSTATION );
}


int getRecommendedWidthOS( const QString& titleText, int totalSystemButtons )
{
// Query the operating system metrics.

int finalWidth = 0;

NONCLIENTMETRICS metrics;
ZeroMemory( &metrics, sizeof( NONCLIENTMETRICS ) );
metrics.cbSize = sizeof( NONCLIENTMETRICS );

if ( SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &metrics, 0 ) != 0 )
{
// Add the width of caption buttons.
// Buttons have this total width or less.

finalWidth += metrics.iCaptionWidth * totalSystemButtons;

// Determine if the Windows version is at least 8, so we
// can centre the caption text.

static bool isWindows8 = setIsWindows8();
if ( isWindows8 )
finalWidth *= 2; // Double the button width to make the title centered.

// ...
The rest of the code remains the same.