Set a custom icon for a QAction when disabled - user-interface

Is it possible to have a custom icon displayed for a QAction when it is disabled? E.g. display icon A when the tool is enabled and icon B when the tool is disabled.

When creating a QAction, you pass it a QIcon. Although I haven't tried this myself, I've noticed that QIcon has a function void QIcon::addPixmap ( const QPixmap & pixmap, Mode mode = Normal, State state = Off ). The Mode can be one of Normal, Disabled, Active, or Selected. Thus, I presume something like this would work:
QPixmap enabled_icon( "enabled.png" );
QPixmap disabled_icon( "disabled.png" );
QIcon icon( enabled_icon );
icon.addPixmap( disabled_icon, QIcon::Disabled );
QAction action( icon, "&Menu action..." );
I would be interested in learning if this actually does work. I've never gotten around to testing it, but it seems like exactly the use this was designed for.

Connect a slot to signal QAction::changed(), then set the icon there if the action is enabled or disabled. (You could do this by subclassing QAction and add the slot in the subclass, connecting it in the constructor).

If someone looking same thing for toogle state of action
QIcon icon;
icon.addPixmap( selectedPixmap, QIcon::Normal,QIcon::On );
icon.addPixmap( normalPixmap, QIcon::Normal,QIcon::Off );

Related

WIN32: Duplicate Standard Button Control (disabled Icon / hotkey underline) in Owner Draw Button?

So for the simple feat of wanting to put an icon on the right side of button text instead of the left resulted in having to use owner draw buttons (but someone here said Custom Draw is actually available if using visual themes). Okay, fine, but now I'm finding you can't really duplicate what Windows standard buttons do when it's not in owner draw mode.
For a normal enabled button I can get the look correct by checking if visual styles are available or not and then using either the DrawThemeBackground() / DrawThemeText() or DrawFrameControl() / DrawText(). However the hot key underline character is shown even when alt key is not pressed, the default buttons don't show it until alt pressed.
For a disabled button, I can't duplicate the disabled look of the icon placed on the button. I tried DrawState() over DrawIconEx() but that looks like the old Windows 3.1 type grey graphic not the visual style dimmed graphic. I see there is a DrawThemeIcon() for an image list, I guess I could try that (I'd have to test non visual style mode to see if DrawState() matches when not using visual styles).
Also, as you hover over the button, the state doesn't change, I understand that if using owner draw, that doesn't occur, maybe it would still work with Custom Draw?
So the two main questions are:
1 - Is there something built-in to the button / owner draw to handle the underlined hotkey only when alt was pressed?
Update to Question 1: I found DT_HIDEPREFIX in DrawText() and using Custom Draw there is the CDIS_SHOWKEYBOARDCUES flag. However with Owner Draw I'm not sure if there is a flag someplace?
2 - How do you draw an icon for a button that is disabled to match what default buttons do?
TIA!!
For shortcut underline you can use WM_QUERYUISTATE to ask if it should be hidden or visible
DWORD draw_text_flags = ...;
if ( SendMessage( control_hwnd, WM_QUERYUISTATE, 0, 0 ) & UISF_HIDEACCEL ) != 0 )
{
// hide prefix
draw_text_flags |= DT_HIDEPREFIX;
}
// some combination of PBS_DEFAULTED, PBS_DISABLED, PBS_HOT, PBS_NORMAL, PBS_PRESSED;
int state = ...;
DrawThemeText( theme, hdc, BP_PUSHBUTTON, state, text, text_len, draw_text_flags, 0, rect );
Answer to Q2: If you create an HIMAGELIST using ILC_COLOR32 | ILC_MASK, and use ILD_NORMAL|ILD_BLEND25 on ImageList_Draw() it gives the same look as windows default buttons for a disabled button.
Based on responses from #Remy-Lebeau and #Daniel-Sęk and reviewing various projects on CodeProject, I create an easy to use class to handle this. It can be found at CodeProject. Thanks Guys.

Third-party plugin that changes host's menu in Win32

I am working on 3rd-party plugin for a desktop Windows app. It is basically a traditional DLL that is only ever accessed by the host application. The host provides menu items for the plugin, based on an API, but I would like to extend the API by modifying the text of my menu items dynamically based on which file is open. The host uses an MDI window and I think it is developed with MFC. However, I am attempting to change the menu items directly with the Win API. (This may be the problem right there, which is part of my question.)
The code works correctly the first time I change the menu item. But subsequent changes do not appear on the menu. The weird thing is that GetMenuString seems always to return the value I set it to. That means that on the API level it appears to work, but the menu items do not change (except for the first time).
Here is the code I use to change the menu item text. It's very basic.
MENUITEMINFOW menuInfo;
memset ( &menuInfo, 0, sizeof(menuInfo) );
menuInfo.cbSize = sizeof(menuInfo);
menuInfo.fMask = MIIM_STRING;
menuInfo.fType = MFT_STRING;
menuInfo.dwTypeData = (LPWSTR)newItemText;
SetMenuItemInfoW (hMenu, idToChange, false, &menuInfo);
Could MFC be interfering with this? Or perhaps an idiosyncrasy of the host app's menu handling? Or is there something else I need to do to get the menu to display correctly?
I got this to work by using DeleteMenu and InsertMenu instead of SetMenuItemInfo. The code is straigthforward, but here it is in case it helps someone else like me.
const UINT idToChange = GetMenuItemID ( hMenu, index );
DeleteMenu ( hMenu, index, MF_BYPOSITION );
InsertMenuW ( hMenu, index, MF_BYPOSITION | MF_STRING, idToChange, (LPWSTR)newItemText );

QSystemTrayIcon leaves behind too many duplicate icons in the tray

When I run and exit my applications, it leaves too many tray icons in the tray instead of just one. I have also setup my application so only one instances can be instantiated at one time but still after several start and exit of the program, system tray seems to accommodate all the icons which than slowly drops when I hover mouse on them to last (legit) one. How can I stop creating this duplicate icons?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
systemTray = new QSystemTrayIcon();
systemTray->setIcon( QIcon(":icons/Resources/Orange.ico") );
systemTray->setVisible( true );
systemTray->show();
systemTray->setToolTip("Rigaku sync application");
systemTrayIconMenu = new QMenu();
systemTrayIconMenu->addAction( ui->actionOpen_App );
systemTrayIconMenu->addAction( ui->actionSettings );
systemTrayIconMenu->addAction( ui->actionClose );
systemTrayIconMenu->addAction( ui->actionQuit );
systemTray->setContextMenu( systemTrayIconMenu );
}
I delete the systemTray pointer in the destructor.
Since we at it, I also want to be able to double click the tray icon which should bring up the app. How can I do that? I understand I have to setup default option on double click (which also appears bold in context menu) but how can I do that? Thanks!
Update
I can show the default menu now with setDefaultAction() and double click on tray. Now my only issue is how to get rid of extra icons in system tray.
If I understood correctly, you are using the C/C++ exit function.
In order to properly quit the Qt application, you'll have to call this function:
QCoreApplication::quit(); // Return code is 0
If you would like to specify the return code, use the following function:
QCoreApplication::exit(YOUR_RETURN_CODE);
You can also use QApplication instead of QCoreApplication, there is no difference.
So, when using one of these methods, the tray icon is correctly destroyed after you exit your application.

How to simulate modal dialog UI behaviour in a modeless dialog?

Is there an easy way to display a dialog modelessly while retaining the UI blocking a modal dialog provides?
I want to stop a user interacting with other dialogs/controls when the dialog is shown, but let the application carry on running. Is there a way to set a dialog as "exclusive focus" or something like that?
No, there is no easy way to do what you want.
If you really want to go the route you describe, I recommend first reading the whole 'modality' series on Raymond Chen's blog. First installment is on http://blogs.msdn.com/b/oldnewthing/archive/2005/02/18/376080.aspx .
However, this seems like an instance of the XY problem. What is it that you are trying to do? Get the main application to keep updating itself? If so, I think (with the information we are given) that calling AfxPumpMessage() will do what you want. Or do you want to continue processing data in the main application? Then you'll save yourself a world of hurt by using a worker thread.
Untested, but you can try do disable the owner window (the app. main window), create a modeless dialog, and then, when the dialog is closed, enable it again:
To disable the main window:
AfxGetMainWnd()->EnableWindow(FALSE);
To create the modal/non-blocking dialog:
dlg->Create(resId)
And to enable it again, on the OnClose event, or similar:
AfxGetMainWnd()->EnableWindow(TRUE);
There may be other details in a modal dialog that I'm not aware of. If you are willing to investigate, read the source code of MFC's CDialog::DoModal(). If I remember correctly, this MFC function simulates a modal-blocking dialog using the modeless Win32 API CreateDialog*() in order to implement global accelerators, hooks, messages and the like.
Here is possible answer to your question:
You could disable all the other controls in the application then re-enable them after the dialog has finished.
Use this callback
BOOL CALLBACK EnableDisableAllChildren ( HWND hwnd, LPARAM lp )
{
::EnableWindow ( hwnd, (BOOL)lp );
return TRUE;
}
With a Call to
EnumChildWindows ( HWNDToYourApp, EnableDisableAllChildren, true );
Do Modaless Dialog
EnumChildWindows ( HWNDToYourApp, EnableDisableAllChildren, false );
Something different to think about.

In X11, how do I set the window title before creating it?

Context:
I use glfw under xmonad. Glfw apparently sets the window title after creating the window, thus not allowing xmonad to properly handle it. I want to modify the glfw source so that I can set the window title before creating the window.
Problem:
So I download glfw-2.6, and I look into lib/x11/x11_window.c ; the lines causing the trouble are:
// Create a window
_glfwWin.Win = XCreateWindow(
_glfwLibrary.Dpy,
RootWindow( _glfwLibrary.Dpy, _glfwWin.VI->screen ),
0, 0, // Upper left corner
_glfwWin.Width, _glfwWin.Height, // Width, height
0, // Borderwidth
_glfwWin.VI->depth, // Depth
InputOutput,
_glfwWin.VI->visual,
CWBorderPixel | CWColormap | CWEventMask,
&wa
);
Followed sometime later by:
_glfwPlatformSetWindowTitle( "GLFW Window" );
where
void _glfwPlatformSetWindowTitle( const char *title )
{
// Set window & icon title
XStoreName( _glfwLibrary.Dpy, _glfwWin.Win, title );
XSetIconName( _glfwLibrary.Dpy, _glfwWin.Win, title );
}
Now, if I tr yto move the glfwPlatformSetWindowTitle call before the CreateWindow call, I get a segfault -- as I should, since _glfwWin.win would not be defined.
I don't know how to solve this problem since to set the window title, I need _glfwWin.Win to be initialized, but to initialize it, I need to create the window.
Thus, I ask: in X11, what is the proper way to set the window title before creating the window?
Thanks!
This is not possible in X11, but also not necessary for stuff to work. There must be a bug somewhere causing the symptoms you're seeing. The window title is just a property on the window, and properties can't exist until there's a window for them to be on.
You say "not allowing xmonad to properly handle it" which implies it isn't coping with changes to the name; window managers absolutely must handle setting the title at any time, including changing the title long after a window is created.
What the spec says (http://www.x.org/docs/ICCCM/icccm.pdf) is:
"The window manager will examine the contents of these properties when the window makes the
transition from the Withdrawn state and will monitor some properties for changes while the window is in the Iconic or Normal state."
The "transition from the Withdrawn state" is the point where glfw calls XMapWindow(). At that point, the window will remain unmapped but the WM will receive a MapRequest. The WM would then read properties and such and then map the window. All window managers I've ever seen also handle later changes to the property because changing the window title is pretty normal. For example web browsers the page title on every url.
If xmonad doesn't handle changes maybe it at least waits for the map, so maybe you just need to set title before XMapWindow(). Really all setup should be done before MapWindow though only a few properties are required to be before it by the specs. The props that must be before it generally can't be changed without unmapping.
Incidentally, _glfwPlatformSetWindowTitle won't work for anything but Latin-1. The modern way to do it is to set _NET_WM_NAME and _NET_WM_ICON_NAME with XChangeProperty() (setting the old Latin-1 WM_NAME is fine too but only as a fallback).

Resources