Test if QToolTip is obsucred before displaying - c++

I'm using a QWindow to contain some heavily optimized OpenGL code.
To facilitate user interaction I have a mode where data under the cursor is displayed in the tooltip.
This leads to a UX problem when another window obscures the one sending the tooltip event. In the following screenshot, the tooltip is draw on top of a Firefox window (which is undesired).
What is the paradigmatic solution? Is there a way to test if part of a window is obscured?
The function calling the tooltip looks something like:
if (!qIsNaN(value_under_cursor))
{
auto state = QApplication::applicationState();
auto text = QString::number(value_under_cursor, 'f', 3);
static QString old_value;
if (text != old_value)
{
auto static last_show = timestamp();
auto now = timestamp();
auto re_raster = ((now - last_show) >= ms_to_chrono(100));
if (re_raster)
{
QToolTip::showText(current_mouse_coordinates_in_global, text);
last_show = now;
}
}
old_value = text;
}

I don't know if there is a cross-platform way to test if specific region is obscured by another apps window, but you can:
check if your window is in focus (QWindow::isActive(), IIRC) and only show tooltip if it is, or
render tooltip using OpenGL.

Related

Closing Popup and setting button label

I'm writing a C++ wxWidgets calculator application. I want to compress trigonometric function buttons into a single one to save on space, using what's basically a split button. If you left click on it, the current option is used. If you right click, a popup menu is opened, which contains all the buttons; when you click on one of them, it is used and the big button changes.
I've been suggested to use wxComboBox and other stuff for this job, but I preferred using wxPopupTransientWindow because this way I can display the buttons in a grid, making everything - in my opinion - neater.
Problem is, when I choose an option from the menu, the main button's ID changes (because when I reopen the menu the previously clicked button is light up as its ID and the big button's ID match), but the label does not. Furthermore, the popup is supposed to close itself when you left click on one of the buttons, but it does not.
This is the code for the custom button in the popup which is supposed to do all that stuff:
void expandButton::mouseReleased(wxMouseEvent& evt)
{
if (pressed) {
pressed = false;
paintNow();
wxWindow* mBtn = this->GetParent()->GetParent();
mBtn->SetLabel(this->GetLabel());
mBtn->SetId(this->GetId());
this->GetParent()->Close(true);
}
}
This is the code for the custom button in the main frame which opens up the popup (temporary setup just to test if the whole thing is working):
void ikeButton::rightClick(wxMouseEvent& evt) // CREA PANNELLO ESTENSIONE
{
if (flags & EXPANDABLE)
{
std::vector<expandMenuInfo> buttons;
buttons.push_back(expandMenuInfo(L"sin", 3001));
buttons.push_back(expandMenuInfo(L"cos", 3002));
buttons.push_back(expandMenuInfo(L"tan", 3003));
buttons.push_back(expandMenuInfo(L"arcsin", 3004));
buttons.push_back(expandMenuInfo(L"arccos", 3005));
buttons.push_back(expandMenuInfo(L"arctan", 3006));
wxPoint p = this->GetScreenPosition();
size_t sz = this->GetSize().GetHeight() / 1.15;
expandMenu* menu = new expandMenu(this, buttons, sz, wxPoint(
p.x, p.y + this->GetSize().GetHeight() + 2));
menu->SetPosition(wxPoint(
menu->GetPosition().x - ((menu->GetSize().GetWidth() - this->GetSize().GetWidth()) / 2),
menu->GetPosition().y));
menu->Popup();
}
}
Let me know if I need to show more code.
This is probably a terrible way of doing this, but this is basically the first "serious" application I'm creating using the wxWidgets framework. Any help is appreciated.
when I choose an option from the menu, the main button's ID changes
(because when I reopen the menu the previously clicked button is light
up as its ID and the big button's ID match), but the label does not.
If you're creating the popup menu like in your previous post, you had a popup window with a panel as its child and the buttons were then children of the panel layed out with a sizer.
If that's still the case, this->GetParent() and should be the panel, this->GetParent()->GetParent() should be the popup. So this->GetParent()->GetParent()->GetParent() should be the trig function button (assuming you created the popup with the trig function button as the parent).
So I think the line wxWindow* mBtn = this->GetParent()->GetParent(); should be changed to wxWindow* mBtn = this->GetParent()->GetParent()->GetParent();.
Or slightly shorter wxWindow* mBtn = this->GetGrandParent()->GetParent();;
the popup is supposed to close itself when you left click on one of
the buttons, but it does not.
It looks like wxPopupTransientWindow has a special Dismiss method that is supposed to be used to close it.
So I think the line this->GetParent()->Close(true); should be changed to this->GetGrandParent()->Dismiss(); (Assuming as above that the buttons in the popup are children pf a panel).
Alternately, if you want a solution that will work regardless of the parentage of the controls in the popup window, you could have a utility function to find the popup ancestor which would look something like this:
wxPopupTransientWindow* FindPopup(wxWindow* w)
{
wxPopupTransientWindow* popup = NULL;
while ( w != NULL )
{
popup = wxDynamicCast(w, wxPopupTransientWindow);
if ( popup )
{
break;
}
w = w->GetParent();
}
return popup;
}
This uses the wxDynamicCast function which is slightly different from the c++ dynamic_cast expression. wxDynamicCast uses wxWidgets' RTTI system to check if the given pointer can be converted to the given type.
Then the mouseReleased method could use this utility function something like this:
void expandButton::mouseReleased(wxMouseEvent& evt)
{
if (pressed) {
pressed = false;
paintNow();
wxPopupTransientWindow* popup = FindPopup(this);
if (popup ) {
wxWindow* mBtn = popup->GetParent();
mBtn->SetLabel(this->GetLabel());
mBtn->SetId(this->GetId());
mBtn->Refresh();
popup->Dismiss();
}
}
}
I'm not sure why you're setting trig function button to have a new id, but I assume you have a reason.
To make the SetLabel method work in your custom button class, I think the easist thing is to call the SetLabel() method in the button's constructor. This will store the string passed to the constructor in the button's internal label member.
Based on other questions, I think the ikeButton constructor looks something like this:
ikeButton::ikeButton(wxFrame* parent, wxWindowID id, wxString text,...
{
...
this->text = text;
To store the label, you would need to change the line this->text = text; to
SetLabel(text);
And when you draw the button, I think the method you use looks like this:
void ikeButton::render(wxDC& dc)
{
...
dc.DrawText(text, ...);
}
You would need to change, the line dc.DrawText(text, ...); to
dc.DrawText(GetLabel(), ...);
Likewise, any other references to the button's text member should be changed to GetLabel() instead.
Finally, when you set the label in the expandButton::mouseReleased method, it might be a good idea to call button's Refresh() method to force the button to redraw itself. I added that my suggestion for the mouseReleased method above.

Text Selection with CRichEditCtrl in MFC

I have CRichEditCtrl object which is read only(Text is for read and not allowed to modified). I want to provide functionality of Text Selection with Mouse for text displayed with CRichEditCtrl object.
Following Code is working to capture event for Left Mouse Button - DOWN & UP
BOOL CReportFormView::PreTranslateMessage(MSG* pMsg)
{
if (m_EditNs->GetFocus()!=NULL)
{
switch (pMsg->message)
{
case WM_LBUTTONDOWN:
return TRUE;
case WM_LBUTTONUP:
return TRUE;
}
}
}
Now looking for some code to write in case block which will highlight selected text. I want to know if there is any API available for CRichEditCtrl object which help to track at which location user pressed Left Mouse Button and released
You could use member function CString strText = m_myRichEditCtrl.GetSelText(); or some other member function. Like GetSel() just a suggestion.
I think you will need to use the EM_CHARFROMPOS message. ex. some form of this:
POINTL pt { x,y }; // ... screen coordinates to test relative to edit
DWORD info = m_EditNS->SendMessage(EM_CHARFROMPOS, 0, pt);
int charIndex = LOWORD(info);
int line = HIWORD(info);
After that, set the selection with normal selection methods.
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761566(v=vs.85).aspx

Global alt+space hotkey grabbing - weird keyboard focus behaviour

I'm grabbing Alt+Space global hotkey using xcb_grab_key, as follows:
xcb_key_symbols_t *keysyms = xcb_key_symbols_alloc(c);
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, XK_space), keycode;
// add bindings for all screens
xcb_screen_iterator_t iter;
iter = xcb_setup_roots_iterator (xcb_get_setup (c));
for (; iter.rem; xcb_screen_next (&iter)) {
int i = 0;
while(keycodes[i] != XCB_NO_SYMBOL) {
keycode = keycodes[i];
xcb_grab_key(c, true, iter.data->root, XCB_MOD_MASK_ANY, keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC);
i += 1;
}
}
Then in Qt's QAbstractNativeEventFilter subclass I process it and emit a Qt signal if key matches Alt+Space:
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, XK_space);
int i = 0;
bool found = false;
while(keycodes[i] != XCB_NO_SYMBOL) {
if(event->detail == keycodes[i]) {
if(event->state & GetModifier(c, keysyms, XK_Alt_L) || event->state & GetModifier(c, keysyms, XK_Alt_R)) {
xcb_allow_events(c, XCB_ALLOW_ASYNC_KEYBOARD, event->time);
emit gotHotKey();
found = true;
} else {
xcb_allow_events(c, XCB_ALLOW_REPLAY_KEYBOARD, event->time);
}
break;
}
i += 1;
}
if(found) return true;
(GetModifier is copied from VLC but I think this part doesn't matter since Alt-key is matched correctly)
The problem I'm having is that after show()ing main window when the hotkey is pressed, keyboard is most of the times1 not focused properly. I can type, but the cursor is not visible, input's border is not highlighted, and the shortcut Ctrl+Q for quitting desn't work. It can be worked around by moving the window, or pressing space - then focus is restored - cursor+border reappears and Ctrl+Q works. What might be causing this behaviour?
I'm using Qt 5.0.0 and xcb 1.8.1. Complete application can be downloaded for compiling from github.
1 it means sometimes the issue is not reproducible - focus is set correctly even for repeated window hide/shows, but then other times it happens multiple times in a row of hide/shows. It occurs more often than not overall.
(Edit: I've implemented a (very ugly...) workaround, so to reproduce the issue for the github project, the following code needs to be removed)
#ifndef WIN32
// Very ugly workaround for the problem described at http://stackoverflow.com/questions/14553810/
// (just show and hide a modal dialog box, which for some reason restores proper keyboard focus)
hackDialog.setGeometry(0, 0, 0, 0);
hackDialog.setModal(true);
hackDialog.show();
QTimer::singleShot(100, &hackDialog, SLOT(reject()));
#endif

MFC VC++ Custom Checkbox Image

How do I get a 3-state checkbox to use a different bitmap for the Indeterminate state?
I want to change the image used by my 3-state checkboxes to use a different one; the controls are in Win98-style, and the indeterminate state of such checkboxes is difficult to distinguish from disabled checkboxes (this is presumably why they changed this for the WinXP-style controls, but I cannot use those because of other details in my project).
I'm using Visual C++ 2010, and I've defined an 8x8 bitmap in VS's Resource Editor. The bitmap's ID is IDB_INDET_CHECK.
I'm not entirely sure what the standard "technique" for something like this is; I've only really just started getting into manipulating Windows controls and MFC.
My first attempt was to create a class, CTriButton, that derives from CButton, override the DrawItem function, and try to draw it myself. I then used SubclassDlgItem to turn one of the checkboxes in my window into this class (I think?). This... sort of works? The checkbox no longer appears, and if I click on where it should be, an empty checkbox frame appears, but nothing else happens (and the debug message in my code is not being sent).
Here's the relevant code, though I'm not sure any of this is right. First, code from my window's OnInitDialog.
BOOL CAffixFilterDlg::OnInitDialog() // CAffixFilterDlg is my CDialog-derived window
{
CDialog::OnInitDialog(); // call basic version
// subclass a CButton-derived control with CTriButton
if ( CBipedHead.SubclassDlgItem(IDC_HEAD, this) ) // CBipedHead is a CTriButton member of CAffixFilterDlg, IDC_HEAD is a checkbox
SetWindowLong(CBipedHead.m_hWnd, GWL_STYLE, CBipedHead.GetStyle() | BS_OWNERDRAW); // set the ownerdraw style
else // subclassing didn't work
_ERROR("Subclassing failed."); // I do not see this error message, so SubclassDlgItem worked?
// initialization continues, but is not relevant...
UpdateWindow();
Invalidate();
return TRUE;
}
Next, the code for my custom button's DrawItem.
void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
_DMESSAGE("Drawing TriButton"); // never see this message
CDC dc;
dc.Attach(lpDrawItemStruct->hDC); //Get device context object
int nWidth = GetSystemMetrics(SM_CXMENUCHECK);
int nMargin = ( nWidth - 8 ) / 2;
CRect textRt = lpDrawItemStruct->rcItem;
textRt.right = textRt.right - nWidth - nMargin;
CString text;
GetWindowText(text);
UINT textDrawState = DST_TEXT;
if ( lpDrawItemStruct->itemState & ODS_DISABLED )
textDrawState |= DSS_DISABLED;
dc.DrawState(CPoint(textRt.left, textRt.top), textRt.Size(), text, textDrawState, TRUE, 0, (CBrush*)NULL);
CRect rt = lpDrawItemStruct->rcItem; // initial rect is for entire button
rt.left = rt.right - nWidth; // set left margin
LONG center = ( rt.bottom + rt.top ) / 2;
rt.top = center - nWidth/2;
rt.bottom = center + nWidth/2;
UINT checkDrawState = DFCS_BUTTONCHECK;
if ( lpDrawItemStruct->itemState & ODS_DISABLED )
checkDrawState |= DFCS_INACTIVE;
if ( lpDrawItemStruct->itemState & ODS_CHECKED )
checkDrawState |= DFCS_CHECKED;
else if ( GetCheck() == BST_INDETERMINATE ) {
_VMESSAGE("Indeterminate; custom draw.");
CBitmap indet_check = CBitmap();
indet_check.LoadBitmap(IDB_INDET_CHECK);
CPoint pt = CPoint(rt.left + nMargin, rt.top + nMargin);
CSize sz = CSize(8, 8);
dc.DrawState(pt, sz, &indet_check, DST_BITMAP|DSS_NORMAL);
}
dc.DrawFrameControl(rt, DFC_BUTTON, checkDrawState);
}
In OnInitDialog() you need to call InvalidateRect() after changing the window style otherwise it doesn't know it needs to be redrawn. It's also a good idea to call UpdateWindow() after changing window styles. Some information is usually cached by the common controls and won't acknowledge the change until UpdateWindow() has been called.
In DrawItem() you are responsible for rendering all states of the control. You should not call CButton::DrawItem() as it does nothing. Try something like the following:
void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CBitmap indet_check
_DMESSAGE("Drawing TriButton"); // I never see this message
int checkState = GetCheck();
if ( checkState == BST_CHECKED )
{
indet_check.LoadBitmap(IDB_INDET_CHECK);
}
else if ( checkState == BST_UNCHECKED )
{
indet_check.LoadBitmap(IDB_INDET_UNCHECKED);
}
else if ( checkState == BST_INDETERMINATE )
{
indet_check.LoadBitmap(IDB_INDET_INDETERMINATE);
}
// ... rest of your drawing code here ...
// don't forget to draw focus and push states too ;)
}
Addendum:
I can't believe I missed this first time around but your call to SubclassDlgItem is probably not having the desired effect. This call causes messages intended for the button to be processed by the controls parent window first. Because the default implementation of DrawItem in CWnd (the superclass of CDialog) does nothing the message never gets passed to the control.
Replace this with the following snippet and everything should be ok:
HWND hWndButton;
GetDlgItem(IDC_HEAD, &hWndButton);
CBipedHead.SubclassWindow(hWndButton);
Two side notes here:
It's usually not a good idea to use the same naming convention for both classes and class members. It makes for a confusing read.
I'm guessing you are always compiling and running in release mode. If you are - don't. This prevents assertions from being thrown and letting you know something is wrong.
Not the answer, but an answer: this custom CCheckBox I found more-or-less enables what I want. It doesn't, by default, allow 3 states, but I fixed that up with some of my own tweaks. I'm not 100% sure it works out of the box (I've had some issues, that don't seem to be due to my edits, but I can't be sure), but it was the solution I've used. I'm not going to call this the answer, though, in case someone can spy what was wrong with my code and wants to illuminate me.

MFC - Printing - Changing page orientation from a custom pagesetup dialog

I am developing a custom print dialog and page setup using MFC and VS2008 for my Win32 program. Since the code is legacy, I can't take much advantage from MFC view/doc architecture. As a result, I wrote a printing code completely from scratch.
I setup CPrintInfo, instantiate my custom print dialog box and hook this dialog box to the CPrintInfo I just created. When my custom print dialog is up, I have a radio button to let a user toggles the page orientation. For some reasons, I couldn't modify the current DEVMODE at the run-time. As a result, every page I print will end up as a portrait.
Even if I manually set pDevMode->dmOrientation to DMORIENT_LANDSCAPE from the event handler of the custom print dialog, the printing result is still ended up as portrait. I am really not sure why this is happening and how to modify the DevMode after the print dialog is up.
Thank you in advance for any help.
Here is the code I have:
void PrintSomething(CWnd* currentWnd) {
// Create CPrintInfo
CPrintInfo* pPrintInfo = new CPrintInfo;
SetupPrintInfo(pPrintInfo); // simply setup some member variables of CPrintInfo
// Create a custom print dialog
CustomPrintDlg* pCustomPrtDlg = new CustomPrintDlg(FALSE, PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS
| PD_HIDEPRINTTOFILE | PD_NOSELECTION, pPrintInfo, currentWnd);
SetupPrintDialog(pPrintInfo,pCustomPrtDlg);
if ( AfxGetApp()->DoPrintDialog(pCustomPrtDlg) == IDOK ) {
... // proceed a print loop
}
}
Code for setting up the custom print dialog:
void SetupPrintDialog(CPrintInfo* pPrintInfo,CustomPrintDlg* pCustomPrtDlg) {
delete pInfo->m_pPD;
pInfo->m_pPD = pCustomPrtDlg;
pInfo->m_pPD->m_pd.hInstance = AfxGetInstanceHandle();
pInfo->m_pPD->m_pd.lpPrintTemplateName = MAKEINTRESOURCE(IDD_CUSTOM_PRTDLG);
// Set the Flags of the PRINTDLG structure as shown, else the
// changes will have no effect.
pInfo>m_pPD->m_pd.Flags |= PD_ENABLEPRINTTEMPLATE;
// Set the page range.
pInfo>m_pPD->m_pd.nMinPage = 1; // one based page numbers.
pInfo>m_pPD->m_pd.nMaxPage = 0xffff; // how many pages is unknown.
}
When a user toggles the radio button to Landscape, this function will be invoked:
void CustomPrintDlg::OnLandscapeChecked() {
// set the current Devmode to landscape
LPDEVMODE pDevMode = GetDevMode();
GlobalUnlock(pDevMode);
pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
}
A pseucode for the custom print dialog class:
class CustomPrintDlg: public CPrintDialog {
... // just override some methods from CPrintDialog
};
Thanks again,
Unagi
I figured out the solution:
All I need is to call GlobalLock to obtain a pointer to the Devmode before changing the current DevMode.
void CustomPrintDlg::OnLandscapeChecked()
{
// set the current Devmode to landscape
LPDEVMODE pDevMode = GetDevMode();
GlobalLock(pDevMode);
pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
GlobalUnlock(pDevMode)
}
Thanks again for helping me.
Nowhere in your example code do you show how you're creating the DC for printing. When you call CreateDC, you must pass a pointer to a DEVMODE structure; this defines whether the printing will be portrait or landscape.