How to know if a CWnd scrollbar is visible? - c++

I have a custom control that displays images. It displays scroll bar when image exceeds the displayable size. For some reason, I want to know whether a scroll bar horizontal or vertical is displayed or not. I can't find any direct functions in CWnd class.
I found GetScrollBarInfo but had no luck checking that. Here is the code:
SCROLLBARIINFO stHSBInfo;
GetScrollBarInfo( this->GetSafeHwnd(), OBJID_HSCROLL, &stHSBInfo );
if( !( STATE_SYSTEM_INVISIBLE &stHSBInfo.rgstate[0] )) // Always true
{
// Code
}
Please help!

Get the size of system scrollbar width with GetSystemMetrics.
Compare the size of your custom control between GetWindowRect and GetClientRect.
If the differences is larger than the size of a scrollbar width, is is visible.

The documentation states, "Before calling the GetScrollBarInfo function, set cbSize to sizeof(SCROLLBARINFO)."
Your code should be updated so:
SCROLLBARIINFO stHSBInfo;
stHSBInfo.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo( this->GetSafeHwnd(), OBJID_HSCROLL, &stHSBInfo );
if( !( STATE_SYSTEM_INVISIBLE &stHSBInfo.rgstate[0] )) // Always true
{
// Code
}

Related

wxStyledTextCtrl - Size of AutoComp

I was just wondering if it is possible to find the size (in pixels) of the autocompletion control shown by the wxStyledTextCtrl.
My goal is to show a help window associated with the entry when a selection happens. Therefore, I need the location and also the width of the autocompletion control. It seems location can be found from m_STC->AutoCompPosStart() but there seems to be no way of finding the width. I am using the following code:
auto StartPos = m_STC->ToPhys(m_STC->PointFromPosition(m_STC->AutoCompPosStart()));
int MaxChars = m_STC->AutoCompGetMaxWidth(); //returns 0 unless set to a fixed value
int w, h;
m_STC->GetTextExtent(wxString("A", MaxChars), &w, &h);
return wxPoint(StartPos.x + w, StartPos.y);
I am using Windows and wxWidgets 3.2.
There is no way to get this information from the styled text control because the autocomp window is completely managed by Scintilla. And unfortunately, Scintilla doesn't make any methods available for getting this info.
As a hack-around, the popup is currently implemented as a child window of the styled text control. So you could do something like this:
const wxWindowList& childred = m_stc->GetChildren();
for ( auto it = childred.begin() ; it != childred.end() ; ++it )
{
// We're assuming the styled text control has at most 1 child -
// namely the autocomp popup. It might be better to check that
// the window found is in fact the auto comp popup somehow.
// win->GetPosition() will return screen coordinates, so to get client
// coordinates, ScreenToClient must be called.
wxPoint psn = m_stc->ScreenToClient(win->GetPosition());
wxSize sz = win->GetSize();
// Do something with size and position here.
}
However, this isn't guaranteed to always work. If in the future, the auto comp popup implementation is changed to use a top level window instead of a child of the control, this method will fail.

Determine the character on the console on witch the MouseCursor is on

As we all know, the console buffer size is composed like a 2D array. I'm trying to implement on click buttons (drawn buttons NOT child windows) but im having an accuracy problem.
Because the Console Window is movable and resizable, i have to take the Mouse Cursor position relative to the Console Window TopLeft corner (I've found a way of accurately doing that in pixels). But now the problem arrives. When i try to find out on which character square the Mouse Cursor is on, it becomes inacurate (errors of about 3 ~ 5 pixels) and this is a problem when implementing on click buttons.
These are the functions i use. Also keep in mind that we need to previously have the GetCurrentConsoleFont() declared. (find it here)
For ease of testing, I have implemented a little "Draw my thing" game in the main (see full code).
/** This returns the cursor position relative to any window (not just the console).*/
POINT GetCursPosRelWin(HWND hWindow)
{
POINT rCoord;
RECT windowCoord;
HWND hConsole = GetConsoleWindow();
GetWindowRect(hConsole,&windowCoord);
POINT ptCursor;
GetCursorPos(&ptCursor);
rCoord.x = ptCursor.x - windowCoord.left;
rCoord.y = ptCursor.y - windowCoord.top;
return rCoord;
}
WORD GetCurrentFontHeight()
{
CONSOLE_FONT_INFO cfi;
GetCurrentConsoleFont(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
return cfi.dwFontSize.Y;
}
WORD GetCurrentFontWidth()
{
CONSOLE_FONT_INFO cfi;
GetCurrentConsoleFont(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
return cfi.dwFontSize.X;
}
So, is there any way of making this method be more accurate?
EDIT: This is the most accurate way i managed to find though it is still not very precise.
/** See the full code for a better understanding */
/** In the main function as parameters of MoveConsoleCursor() */
MoveConsoleCursor(
(SHORT)((double)(ptCursor.x/GetCurrentFontWidth() - ((ptCursor.x/GetCurrentFontWidth())%10)/10 )),
(SHORT)((double)(ptCursor.y/GetCurrentFontHeight() - 0.5))
);
You can change your GetCursPosRelWin to:
POINT GetCursPosRelWin(HWND hWindow)
{
POINT ptCursor;
GetCursorPos(&ptCursor);
ScreenToClient(hWindow, &ptCursor);
return ptCursor;
}
And MoveConsoleCursor call to:
MoveConsoleCursor(ptCursor.x / GetCurrentFontWidth(), ptCursor.y / GetCurrentFontHeight());
This puts cursor in the center of a square, provided the scroll bars are not moved. Otherwise you have to account for the scrollbar offsets.

How to stop the bottom scrollbar from a CListCtrl from displaying?

I have a CListCtrl which is dynamically resized with the dialogue. I used a WM_SIZE message handler in the derived CListCtrl to resize the columns such that the total is the width of the control - 4, where the - 4 is to indicate the width of the border.
When I make the dialogue bigger, the control resizes correctly and I don't get the bottom scrollbar. However when I shrink the control, I sometimes get the horizontal scrollbar showing up.
void CMyListCtrl::OnSize(UINT nType, int cx, int cy)
{
CListCtrl::OnSize(nType, cx, cy);
ResizeLastColumn();
}
void CMyListCtrl::ResizeLastColumn()
{
LVCOLUMN column;
column.mask = LVCF_WIDTH;
LONG maxWidth = 0;
for (int i = 0; i < lastColumnIndex; ++i)
{
GetColumn(i, &column);
maxWidth += column.cx;
}
CRect wndRect;
GetWindowRect(&wndRect);
SetColumnWidth(lastColumnIndex, wndRect.Width() - maxWidth - 4);
}
It is like the WM_SIZE message is getting to the control before the control is finally resized.
This is related to How to determine if a scrollbar for a CListCtrl is displaying?. However, this question is not dealing with the right scrollbar, and is assuming that it is not being displayed.
Resizing the window generates a message to test for horizontal scroll. SetColumnWidth will also generate the same message. It depends how ListView handles this internally, but a vertical scroll could also come in and go, this will change the client area, so the code may have to make recursive calls to figure out if the scroll should be visible or not. You can see this can easily run in to problems.
Try resizing the columns in WM_WINDOWPOSCHANGED, before calling the default procedure. Use SetRedraw to stop redundant paint messages.
ON_WM_WINDOWPOSCHANGED()
...
void CMyListCtrl::OnWindowPosChanged(WINDOWPOS *wpos)
{
SetRedraw(FALSE);
ResizeLastColumn();
SetRedraw(TRUE);
CListCtrl::OnWindowPosChanged(wpos);
}
You can use GetClientRect for the client area, then you don't need to subtract the border thickness (which is not always 4).
void ResizeLastColumn()
{
int maxwidth = 0;
int index = GetHeaderCtrl()->GetItemCount() - 1;
for(int i = 0; i < index; ++i)
maxwidth += GetColumnWidth(i);
CRect rc;
GetClientRect(&rc);
SetColumnWidth(index, rc.Width() - maxwidth);
}
Also GetHeaderCtrl()->GetItemCount() returns the number of columns.
I'm facing the exact same issue, with the exact same Use Case.
To disable the horizontal scroll bar altogether, you add a message handler for WM_NCCALCSIZE
Using the Class Wizard this adds the following to your message map:
ON_WM_NCCALCSIZE()
In the implementation of this handler, you modify the style to disable the horizontal scroll bar.
void CMyListCtrl::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
ModifyStyle(WS_HSCROLL, 0); // disable the horizontal scroll bar
__super::OnNcCalcSize(bCalcValidRects, lpncsp);
}
In my own implementation of ResizeLastColumn(), which is similar to yours, I subtract ::GetSystemMetrics(SM_CXVSCROLL) from the width to account for the vertical scroll bar.
I realise this reply is rather late, but hopefully it will help someone.
(Edited to remove mentions of some problems I am having because a) they are off-topic and b) because I think they stem from me not using the Doc-View architecture and things not being wired up correctly as a result. I'm going to re-think my approach.)

Make a QDialog appear in a different screen

The title says it pretty much all:
I have two screens, and each time I create a QDialog it appears in the same screen as its parent.
How can I make it appear in a different screen? Or should I use a different type of top-level widget?
The code I use to create the dialog is:
QDialog my_dialog = new QDialog(this,
Qt::WindowMaximizeButtonHint |
Qt::WindowCloseButtonHint);
...
EDIT:
I have also tried using the QDesktopWidget which gives me a QScreen object that refers to the second screen. But then I don't find how to instruct the QDialog to use that QScreen (setting it as the parent doesn't work).
It is bad, that you edit your question without reading comments :(
// Your screen geometry:
QRect buildScreenGeometry()
{
auto desktop = QApplication::desktop();
QRect virtualRect;
const auto n = desktop->screenCount();
for ( auto i = 0; i < n; i++ )
virtualRect |= desktop->screenGeometry(i);
return virtualRect;
}
// Moving
auto dlg = new QDialog( someParent );
auto newPoint = QPoint( 2000, 0 ); // point on another screen
auto realPos = someParent->mapFromGlobal( newPoint );
dlg->move( realPos );
That's all.
UPDATE:
You should understand, that there are only ONE screen area with COMMON coordinate system, that contains ALL screens.
For example, you have 2 monitors with 800x600 resolution. First (main) monitor is standing left, and second standing right. In this case, coordinate system, that is available for your application is 1600x600. So, if your widget has 100x100 top-left position, on a first monitor, and you want to move it to another, you should call move(900x100); // 900 == screen1.width() + dialog.pos().x(). Then your widget will have 100x100 position on second monitor.
You should read Qt documentation.
You can use move on your QDialog, but be aware that move will set the QDialog position relative to it's parent.
You can get the Main Window's screen position and use that to setup the QDialog position. Just know that you're not guaranteed to have 2 screens on the end user machine.
For more information on move see: http://doc.qt.io/qt-5/application-windows.html#window-geometry

Horizontal scroll not coming for listbox?

I am having a listbox where I set both the properties i.e, vertical and horizontal scroll to true.I am able to get vertical scroll bar but not able to get horizontal scroll bar when added a lengthy string.
Can anyone please let me know how get horizontal scroll bar for a listbox.
Adding this piece of code in OnInitDialog resolved my issue.
BOOL OnInitDialog()
{
CString str;
CSize sz;
int dx = 0;
CDC* pDC = m_listbox.GetDC();
for(int i=0; i < m_listbox.GetCount();i++)
{
m_listbox.GetText(i,str);
sz = pDC->GetTextExtent(str);
if(sz.cx > dx)
dx = sz.cx;
}
m_listbox.ReleaseDC(pDC);
if(m_listbox.GetHorizontalExtent() < dx )
{
m_listbox.SetHorizontalExtent(dx);
ASSERT(m_listbox.GetHorizontalExtent() == dx);
}
return TRUE;
}
You have to specify the horizontal scroll extent (max width in pixels). Do that by calling CListBox::SetHorizontalExtent.
In MFC, I had a listbox that was too big and extended past the right border of the window containing it. Once I made the listbox x dimension fit inside the window, the scrollbar started working properly again.
For some reason if the listbox is too big, Windows did not handle the scrollbar visibility properly as a side-effect of the sizing.