How to create a bold and italic label in MFC? - c++

Please do not mark it as a dupe of this question just yet:
Bold labels in MFC
That question does not help me; for some reason I do not see the rich edit control. Instead I believe I have to do it in code. here is a sample I found:
http://www.tech-archive.net/Archive/VC/microsoft.public.vc.mfc/2006-10/msg00245.html
My problem is that I prefer not to re-invent the wheel and test for errors myself or through QA.
Someone must have implemented this before. Please share your code.
What I would like to do is:
Keep the same font size, family, etc. as in the already created label, but make it bold and italic as well.
Keep the memory footprint reasonably low (do not create any new unnecessary objects), but do not get the app into an inconsistent state either.
I appreciate your help.

You will want to do the following before the static text control is shown on the parent window.
Get a handle to the window: CWnd * pwnd = GetDlgItem(IDC_LABEL);
Get the current font for the static text: CFont * pfont = pwnd->GetFont();
Get the characteristics of the font: LOGFONT lf; pfont->GetLogFont(&lf);
Change the lfWeight and lfItalic fields in lf.
Put a CFont object in your parent window, so it will exist for the entire lifetime of the child window.
Initialize the CFont: m_font.CreateFontIndirect(&lf);
Set the font into the static text window: pwnd->SetFont(&m_font);

Related

How can I adjust the size of a QDialog according to its title length?

One of my dialog window's title is shortened (like "My Dialogt..."). If the dialog was slightly wider, the whole title would be completely displayed, which would look nicer.
It seems as if there is no setting in Qt to do that. I have found a hack for a QMessageBox here: Can QMessageBox::about adjust size to title length?, but it is not general. For example it would have to take also the sizes of the icons to the left and to the right of the window title into account to compute a really good minimal size where still the title is completely shown.
Is there a general way to accomplish that? Is there also a simple way to do that? Or is this overengineering?
Not only this goal is questionable (see vahanco comment) but it is hard to achieve, because the window title bar is not Qt territory at all: apart from being able to set its text and manage to show or hide close/min/max button using window flags, there is little else in control, there.
By the way, a very raw way to set a dialog minimum width which could (could) make room to the whole text is the following:
const QString text = "Very very very very very very very very very very very very very long window title";
setWindowTitle(text);
QFontMetrics metrics(font(), this);
setMinimumWidth( metrics.horizontalAdvance(text));
This won't work out of the box, and it's very likely that the text stay cut, because the font used is supposed to be the same used in the title bar (which usually isn't) and we're not taking into account the frame width, the icon width, the title bar buttons width, and everything else which is owned by the window manager and is totally unknown to Qt.
So, you can figure out how much extra space is needed by all these stuff, and adjust the width with a totally arbitrary extra padding like
setMinimumWidth( metrics.horizontalAdvance(text) + 256);
and maybe get what you wanted in the first place (if you still really want it).
The accepted answer did not work for me.
The below code works in QT 5.15. According to the documentation after you call setMinumumWidth() you must call updateGeometry() update geometry docs. Setting minimumWidth should update the sizeHint. That was not happening for me. Also QFontMetrics::horizontalAdvance was not returning the width of the text. I had to use QFontMetrics::boundingRect({title_string}).width().
Calling resize on the dialog is what finally got it working for me. If the accepted answer doesn't work for you give this a try.
QString message = "Message for the user";
QInputDialog dialog = QInputDialog(this);
dialog.setLabelText(message);
QString longTitle = QString("Super long test title for making sure the widget will show all of the stupid long title.");
dialog.setWindowTitle(longTitle);
dialog.setInputMode(QInputDialog::TextInput);
auto fontMetrics = dialog.fontMetrics();
auto width = fontMetrics.boundingRect(longTitle).width();
dialog.resize(width + 200, dialog.rect().height());
const int ret = dialog.exec();

Create window without title bar

I am trying to create a simple panel for Openbox in Arch Linux using c++, but I cannot figure out how to remove the title bar from a window.
I am creating the window with XCreateWindow(...), and that gives a window with the correct size, but it contains a title bar, and the window also opens in the top-left corner of the screen, no matter what offset coordinates I specify.
I read here that both of these problems are probably caused by the window manager (Openbox), which overrides the window attributes I specified in XCreateWindow(..., &window_attributes). This could be solved by adding window_attributes.override_redirect = True;, although this does not seem to do anything for me. When I try this I get the exact same window as before. (I did compile the file after this change.)
Also I read into the code of Tint2 (link), which is another panel for Openbox. They create a window using the following code:
XSetWindowAttributes att = { .colormap=server.colormap, .background_pixel=0, .border_pixel=0 };
p->main_win = XCreateWindow(server.dsp, server.root_win, p->posx, p->posy, p->area.width, p->area.height, 0, server.depth, InputOutput, server.visual, mask, &att);
I don't see an override_redirect anywhere in their code, so I'm not sure how they are removing the title bar.
As additional information, I thought it would be worth mentioning how I'm executing the script:
/* The c++ file is saved as 'panel.cpp' */
$ gcc panel.cpp -lX11 -o panel
$ ./panel
Also, I am running Arch Linux through VirtualBox with Windows 8 as host. I'm not sure if this changes anything, but it won't hurt to mention.
Since I found the solution, I figured I'd post the solution here if anyone else needs it.
As #JoachimPileborg mentioned, I needed to alter the Openbox settings in ~/.config/openbox/rc.xml. Inside the <applications> tag, I added the following code:
<application class="*">
<decor>no</decor>
<position force="no"></position>
</application>
The class="*" means that all applications will follow these rules, you could fill in the class name of the application instead. The <decor>no</decor> removes the title bar, and <position force="no"></position> ensures that my own script is able to handle the positioning. You could also add another <application> tag after this one to make exceptions to this rule.
Also, the window_attributes.override_redirect = True; is not needed anymore.
A more correct way is to use the Extended Window Manager Hints.
The idea is that you don't tell the window manager how to decorate or not your window, you just indicate the window type with _NET_WM_WINDOW_TYPE :
Atom window_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
long value = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", False);
XChangeProperty(display, your_window, window_type,
XA_ATOM, 32, PropModeReplace, (unsigned char *) &value,1 );
"Dock" is the type for panels and taskbar. Usually they are undecorated and appear on all desktops. As written on the documentation, previously the _MOTIF_WM_HINTS property was used to define the appearance and decorations of the window. Window managers still support it, but _NET_WM_WINDOW_TYPE is prefered as it describe the function and let the window manager (and user) decide on the appearance and behavior of that type of window.
Another interesting property for a panel is _NET_WM_STRUT_PARTIAL, to "reserve" space.

ChooseFont dialog: munges font name fails to reload it

I've found some slightly odd, and more importantly, inconsistent behavior from Win32 ChooseFont() API.
LOGFONT lf = { 0 };
strcopy(lf.lfFaceName, m_face_name);
const int ppi = GetDeviceCaps(pView, LOGPIXELSY);
lf.lfHeight = -MulDiv(m_font_height, ppi, 72);
CFontDialog fd(&lf);
if (fd.DoModal() != IDOK)
return;
m_face_name = fd.GetFaceName();
m_font_height = lf.lfHeight;
Assuming that the first time though, face name is "Segoe UI", this works.
But if the user changes the dialog to be "Segoe UI", "Light", "9", (face, style, height), and we go through the above a second time, then the font choose common dialog fails to select "Segoe UI" as the face name. Instead, I get the Font: field as blank.
This is not a problem if the user selects a style of "Regular", "Italic", "Bold", "Bold Italic", as those are stored in the style bits, and don't munge the name. I discard them for the second run, because I'm ignoring them (I would disable Font Style: if there were a way to easily do so - I don't wish to subclass CFontDialog for this - that's a whole 'nother level of time & effort that this moment doesn't allow for).
I've tried creating a font based on the previous specifics from the dialog, and then tried pulling the LOGFONT back out of that. No dice.
Similarly, I've tried querying the dialog for the FontStyle() - but that returns blank - so nothing to strip from the font name here...
This just seems like a bug with MS's dialog - it tells me one thing, but then cannot use it's own output to correctly initialize itself the second time through (granted, I'm only persisting some, not all, of the LOGFONT in this situation).
Does anyone know WTH is up with this? Or an approach I might use to (short of hard coding looking for " Light" on the end of a font name - YUCK!)?
Developments in font design has significantly outstripped the legacy api's ability to keep up. OpenType happened, for one. There are additional font styles beyond what LOGFONT can support. For Segoe UI, properties that control boldness can be Light and Semibold. For other fonts, font stretch is another property, common ones are Condensed and Expanded. With the font being able to implement a dedicated font file to make these styles look good and not depend on synthesizing the style from an existing font as it was done in the olden days. Review the WPF FontStretch and FontWeight enumeration types for possible values.
These are properties that LOGFONT can't express. There's a compatibility hack to deal with this, type face names get mapped. So "Segoe UI" with a style of "Light" becomes "Segoe UI Light". And the Windows font mapper will pick the right true-type font file from such a name. What however doesn't work is initialize LOGFONT.lfFaceName with "Segoe UI Light". Not actually sure why, it was probably avoided to not have to deal with the ambiguity. Or just plain flubbed. A possible workaround is to recognize these appended style names in the font face name, but that's not perfect either.
GDI is running out of gas. Much like User32.
First, when you initialize your LOGFONT in your proc, you can't just set it to 0
ala "= { 0 };"
Use something like
memset(&lf, 0, sizeof(lf));
Otherwise your lf in your proc contains random crap. Secondly, what's the big deal about not saving all the settings of the LOGFONT structure? It you're using MFC, it's not because it would be too much overhead. If fixing the initialization of lf doesn't work, just save the entire LOGFONT.

How can I change the default CDialog font for a non-modal dialog?

It is necessary to switch off the "ClearType" property of the default font for all dialog controls. It is possible to do that for one control by setting
logfont.lfQuality = ANTIALIASED_QUALITY
There are a lot of suggestion how to do the same for modal dialogs (http://neelaakash.wordpress.com/2007/12/31/change-default-dialog-font-of-cdialog/ and others), but that should be done for non-modal dialogs (are instantiated with new and Create(...) methods). I've tried to do that myself:
Override 'Create' method, and modify dialog template:
BOOL CActivationChildDialogLicenseInfo::Create(UINT nIDTemplate,
CWnd* pParentWnd)
{
CDialogTemplate dlt;
int nResult;
// load dialog template
if (!dlt.Load(MAKEINTRESOURCE(nIDTemplate))) return -1;
// set your own font, for example “Arial”, 10 pts.
dlt.SetFont(L"Arial", 12);
// get pointer to the modified dialog template
LPSTR pdata = (LPSTR)GlobalLock(dlt.m_hTemplate);
// let MFC know that you are using your own template
m_lpszTemplateName = NULL;
InitModalIndirect(pdata);
// display dialog box
nResult = CActivationChildDialog::Create(nIDTemplate, pParentWnd);
// unlock memory object
GlobalUnlock(dlt.m_hTemplate);
return nResult ;
}
Seems like this method do nothing (it is called, I've checked that with putting break-point inside).
I've tried to call
nResult = CActivationChildDialog::Create(NULL, pParentWnd);
...but got a lot of ASSERTs.
I've also tried to override the 'OnSetFont' method:
void CActivationChildDialogLicenseInfo::OnSetFont(CFont *pFont)
{
CActivationChildDialog::OnSetFont(pFont);
LOGFONT logfont;
pFont->GetLogFont(&logfont);
LOGFONT logfont2=logfont;
pFont->DeleteObject();
logfont2.lfItalic = true;
logfont2.lfQuality = ANTIALIASED_QUALITY;
pFont->CreateFontIndirect(&logfont2);
}
That causes ASSERT during run-time and resulted in a VERY big font being used (lost default font settings, doesn't accept new specified settings)... I don't know why.
Please advise, how can I change a default dialog font that will be "inherited" by all dialog controls?
Thank you very much.
First off: the simple, reliable way to do this is to create the dialog and then send WM_SETFONT (or call SetFont()) to the dialog and each control in it. I'll show you how to do that below, but first, here's why the two strategies you've already tried didn't (and can't) work:
Modifying the dialog template
First off, you should be calling CDialog::CreateIndirect() if you wish to use a dialog template that you've already loaded.
But don't bother. The dialog template contains only the face name and point size - it does not allow you to specify other LOGFONT values such as lfQuality. If it did, you could simply specify that in your resource definition and avoid writing any runtime code at all!
Intercepting WM_SETFONT
In theory, you could make this work. But it's not practical. Your code has several problems: first off, you'd have to intercept this message for every child control in order for it to do anything useful: the dialog itself probably doesn't render any text. But worse, you're passing the original font to the base class (which hands it to the default window procedure, which stores it internally for later use) and then immediately destroying it - this means the dialog (and everything else using that font, including all of the child controls) will be trying to draw text using a bogus font, and reverting to the default font as a result. Finally, you're creating a new font attached to a temporary object (pFont) created and destroyed by MFC - internally, the CFont object you're working with will be detached from the font handle and destroyed, leaking a handle to a font object that nothing uses.
Leaky abstractions: a note on HFONT and CFont
HFONT is a handle type that Windows uses to represent a font object. Like most of GDI, there are specific functions for creating fonts, and the general-purpose DeleteObject() function for destroying them.
CFont is a light-weight wrapper class for HFONTs. A CFont instance can be attached and detached from an existing HFONT, or used to create a new one. If a CFont instance is still attached to a HFONT when its deconstructor executes, it will call DeleteObject() to destroy the underlying object. Internally, MFC utilizes temporary CFont instances that are attached and detached from HFONTs when calling various message handlers (such as OnSetFont). It's worth remembering that internally, Windows knows nothing about CFont, and a single HFONT may belong to 0 or more CFont instances at any given point in time.
A note on fonts and WM_SETFONT
When you create a new font - whether or not it is wrapped in a CFont object - you are the owner of that font, and it is your responsibility to destroy it once you are finished using it. Passing it to WM_SETFONT (CWnd::SetFont()) doesn't change ownership! This is actually quite useful, as it allows you to pass the same font to multiple windows without worrying about which one will destroy it - you're still the owner, and so you can (and must) destroy it yourself (once there are no windows still using it).
Finally - how to quickly create and set a font on a dialog and all its children
So you should now have enough background to understand the necessary steps:
Create the dialog
Create the desired font
Pass the font to the dialog and its children (by either sending WM_SETFONT messages, or by calling CWnd::SetFont... which itself send a WM_SETFONT message).
When the dialog is destroyed, also destroy your font.
Example
// define this as a class member - class destructor then handles step four!
CFont m_nonCleartypeFont;
BOOL CActivationChildDialogLicenseInfo::Create(UINT nIDTemplate,
CWnd* pParentWnd)
{
// step one: create dialog normally
BOOL nResult = CActivationChildDialog::Create(nIDTemplate, pParentWnd);
// step two: create custom font
// relying on destructor to destroy font once we're done with it
// so be careful to only create it once!
if ( NULL == m_nonCleartypeFont.m_hObject )
{
CFont* pOriginalFont = GetFont(); // use template font as... template!
// pull information from original font
LOGFONT logfont;
pOriginalFont->GetLogFont(&logfont);
// make font adjustments:
// specify italics
logfont.lfItalic = true;
// and non-cleartype antialiasing
logfont.lfQuality = ANTIALIASED_QUALITY;
// create our font based on adjusted information
m_nonCleartypeFont.CreateFontIndirect(&logfont);
}
// step three: set our custom font on the dialog and all children
SetFont(&m_nonCleartypeFont, FALSE);
// Send message to quickly set this font for all children.
// See documentation for SendMessageToDescendants()
// - this is actually the example given!
SendMessageToDescendants(WM_SETFONT,
(WPARAM)m_nonCleartypeFont.m_hObject,
MAKELONG(FALSE, 0),
FALSE);
return nResult;
}

How do I display a tooltip for a CMFCRibbonButton in the status bar?

I have a CMFCRibbonStatusBar in my mainframe to which I add a CMFCRibbonButtonsGroup which again has a CMFCRibbonButton. This button has the same ID as a menu entry.
Creating the button is done as follows:
CMFCRibbonButtonsGroup* pBGroup = new CMFCRibbonButtonsGroup();
CMFCToolBarImages images;
images.SetImageSize(CSize(32, 16)); // Non-square bitmaps
if(images.Load(IDB_STATUSBAR_IMAGES))
{
pBGroup->SetImages(&images, NULL, NULL);
}
m_pStatusButton = new CMFCRibbonButton(ID_STATUS_SHOWSTATUS,
_T(""),
IMAGEINDEX_DEFAULTSTATUS);
pBGroup->AddButton(m_pStatusButton);
m_wndStatusBar.AddExtendedElement(pBGroup, _T(""));
I want to use this button as a status indicator.
I want to display a tool tip in the following two cases:
when the status changes and
when the user moves the mouse over the button.
I have no idea how to start in the first place. I have looked at the ToolTipDemo and DlgToolTips sample projects but couldn't figure out how to do it since all they do is display tooltips for the toolbar items or dialog buttons (CWnd-derived instead of CMFCRibbonButton).
If you are familiar with the ToolTipDemo sample project: Since there seem to be several ways of doing things, I would prefer the tooltip to look like the "Extended Visual Manager-based" tool tip as shown in this screenshot.
Thanks!
I don't think it's possible to show the tooltip without the mouse cursor being over the control. That's all done automatically.
However if you want to have a nice looking tooltip like in your screenshot, you need to call SetToolTipText and SetDescription, like this:
CMFCRibbonButton* pBtn = new CMFCRibbonButton(12345, _T(""), 1);
pBtn->SetToolTipText("This is the bold Title");
pBtn->SetDescription("This is the not-so-bold Description");
pGroup->AddButton(pBtn);
I am using CMFCRibbonButton controls within a CMFCRibbonButtonGroup, which is added to the CMFCRibbonStatusBar. Take note of the 4th parameter in the CMFCRibbonButton() constructor, bAlwaysShowDescription, as this seems to affect the behavior depending upon whether SetDescription() has been called.
Specifically, if SetDescription() has not been called, it doesn't matter whether bAlwaysShowDescription is TRUE or FALSE - the tool tip is displayed (as I would expect). If SetDescription() is set and bAlwaysShowDescription is FALSE, when hovering over the button the tool tip is displayed with the description below it.
What seems counterintuitive given the name of this bAlwaysShowDescription parameter, is that when this is TRUE and SetDescription() is set, NEITHER the tool tip nor the description appear. I wonder if this is related to this post:
https://connect.microsoft.com/VisualStudio/feedback/details/399646/cmfcribbonbutton-wont-show-tooltip-if-balwaysshowdescription-1
Hope this helps and you can achieve what you need with the different combinations of bAlwaysShowDescription parameter and whether SetDescription() is set.