Nested panel not scrolling as expected. Why not? - c++

I am creating a GUI using C++ and winapi. I have been able to work out a lot of details, but I'm having a bit of trouble with the scrolling of panels within another panel. I know how to do this in c#, but with c++ I am defeated.
I have created class which generates a panel for a generic "Topic" and I can designate the name of the topic (e.g., "Topic A"). I have create a separate class which generates a Panel to contain all of these topics. That is the TopicHolder. So I pass in a vector with the names as strings. I then generate new Topic objects, each with their own GUI control. I then update the layout of TopicHolder, including all of the Topics, giving them x and y locations based on their standard size.
Everything looks more or less like I want it to initially. If this was c# the topic holder would scroll and the various topics would scroll in and out of sight depending on the exact portion of the larger panel which was visible. In this case, however, scrolling has absolutley no effect. This is my first time working with scrolling on a c++ gui, so I might be missing something.
Below are some of the key code segments. I'd welcome any helpful tips.
Thanks
Adding the topics
void TopicHolder::SetTopics(std::vector<std::string> t)
{
for(unsigned i=0;i<t.size();i++)
{
//I give the topic the HWND of the TopicHolder so it can
//be properly parented on that GUI element
Topic* tmp = new Topic(myHWND, t[i]);
vectorOfTopics.push_back(tmp);
}
UpdateLayout(holderRect);
}
Handling topic layout relative to TopicHolder
void TopicHolder::UpdateLayout(RECT r)
{
int buffer = 5;//buffer between elements
int x = buffer;
int y = buffer;
for(unsigned i=0;i<vectorOfTopics.size();i++)
{
RECT tmp;
tmp.left=x;
tmp.top=y;
tmp.right = r.right-(2*buffer);
tmp.bottom = y+Topic::HEIGHT;//standard height per topic
vectorOfTopics[i].Layout(tmp);
y+= Topic::HEIGHT+buffer;
}
}
Layout in Topic object
void Topic::Layout(RECT r)
{
SetWindowPos(myHWND, //this is the HWND of the topic
HWND_TOP, r.left, r.top, Topic::WIDTH, Topic::HEIGHT, SWP_SHOWWINDOW);
}
-------------------Edit-----------------------------------------
Grr... Okay, so C++ has no support for this as many of you have explained. I have (painfully) been learning to set up my scroll parameters. Mostly it is working as expected, but I still have a problem with the scrollbar. I expected the scrollbar to show up on the right side of the panel and leave the rest of the space for my Topic panels. Instead it is slightly offset and the rest of the area is painted a light gray rather than the background color of the TopicHolder. Right now it's just irritating, but I would like to see it resolved. Again, any help would be appreciated.

Starting y position of topics in UpdateLayout always remains 5, so no scrolling happens. You should keep some offset value and change it if scrolling up and down happens so y will start from this offset.

You need to call SetScrollPos() to change the scrolling position or the window contents.
i.e:
// hwnd is your control window, SB_VERT refers to its verti8cal scrollbar.
SetScrollPos(hwnd, SB_VERT, topicHeight * topicToBringToTop, TRUE);
InvalidateRect(hwnd, NULL, TRUE); // redraw the control.

Related

How to prevent my app's window from going to another monitor (Electron + macOS)

I've got an app that programmatically moves its window around the user's screen. My problem:
User has two physical monitors
User starts app in primary monitor
App moves window, causing more of it to "overflow" into the secondary monitor than is on the primary monitor
This causes the app's window to entirely jump to that secondary monitor and disappear from the primary monitor.
I would like for the window to stay in the primary monitor unless I want it to go to the secondary monitor, or some defined point (e.g. the center) goes into the secondary monitor, or it would even be ok if the window were split and shown on both. Is there any way to prevent this jump from happening as soon the window intersects more with the secondary monitor?
To reiterate, I'm moving the window programmatically and am using Electron on macOS.
Also, this doesn't appear to happen when I move window manually, but I think this is because it's not using its percentage rule but going on whether or not the point of the mouse has entered the secondary monitor.
Also, I'm open to any kind of solution, including C/C++ or Swift.
EDIT: Here's how I'm currently moving the window:
win.setPosition(x, y);
where win is an instance of Electron's BrowserWindow.
The desired behavior is to move the window anywhere on a display. Currently, if some of the window goes off the edge of the current display enough, it jumps to another display. This is because Apple's default behavior it to automatically move a window to the display with which it overlaps the most and hide it on all other displays. It is worth noting that this is not the behavior when dragging a window manually, but just when moving it programmatically.
I don't know how you are moving this window, or how you want the window to act when it hits the edge, but I will do my best to give you some solutions.
My first thought was for you to create the bounds of the screen when the program starts.
So maybe use GetSystemMetrics(), screen.getPrimaryDisplay(), or perhaps this pseudo-code helps:
var canvas = GetWindowSize()
var canvasWidth = canvas.width
var canvasHeight = canvas.height
if myWindow.x + mywindow.width > canvasWidth
//if you want it to bounce off the edge
then myWindow.direction = oppositeDirection
//if you want it to wrap to the other side of the screen
then myWindow.X -= canvasWidth
You should check out this code of a Bouncing DVD Logo.
Or look at my Asteroids game to see how ship and asteroids move in the screen.
Furthermore, you can use the distance formula to have the window react to the edge when the center gets close. (Although, this would also require you to get the screen bounds x and y.)
Hope that helps!
EDIT:
I think if you were to somehow use the entire screen, (both monitors) your window wouldn't even recognize that there is an edge. That being said, I still don't know what your code actually does.
It's not working when you do win.setPosition(x, y); because of mac OS behaviour.
(I also tried with electron quick start project https://github.com/electron/electron-quick-start, even with the same electron version 6.0.5)
What I did is to programmatically simulate a mouse drag of your app to the right of the screen. To do it you can use robotjs. You will not see the dragging effect, which is pretty close to do a setPosition(x, y)
When you install it you could have an issue when starting you app. Then you have to rebuild rebotJS for your version of electron. https://github.com/octalmage/robotjs/wiki/Electron
So what you will try to do programmatically is
Working code
// get your app bounds and screen bounds
const windowBounds = mainWindow.getBounds()
const displayBounds = electron.screen.getPrimaryDisplay().bounds
setTimeout(() => {
// 1. move mouse upon the top left corner your app
robot.moveMouse(windowBounds.x + 100, windowBounds.y + 5);
// 2. mouse left key, 'down'
robot.mouseToggle("down", "left");
// 3. drag window to (100, 100) from default
robot.dragMouse(displayBounds.width, 0);
// 4. mouse left key toggle 'up'
robot.mouseToggle("up", "left");
}, 100);
Example in main.js: https://playcode.io/electron
I use those version:
"devDependencies": {
"electron": "^6.0.5"
},
"dependencies": {
"robotjs": "^0.6.0"
}
and my versoon of nodejs is v10.20.0
Maybe it's not quite what you asked for, but here's a thought: why not resize the window instead of letting it "overflow" to other screens?
When you predict that window will go out bounds of current display, just reduce its size.
It can be done by something along this lines:
const winWidth = 800;
const winHeight = 600;
const SAFETY_MARGINS = 10;
let mainWindow;
// setPosition //////////////////////////////////////////////////////////////////////////
function setPosition(x, y) {
const displayBounds = screen.getPrimaryDisplay().bounds
const fixedX = Math.min(
displayBounds.x + displayBounds.width,
Math.max(x, displayBounds.x)
);
const fixedY = Math.min(
displayBounds.y + displayBounds.height,
Math.max(y, displayBounds.y)
);
mainWindow.setPosition(fixedX, fixedY);
const fixedWidth = Math.max(SAFETY_MARGINS, Math.min(winWidth, displayBounds.width + displayBounds.x - x, winWidth - displayBounds.x + x));
const fixedHeight = Math.max(SAFETY_MARGINS, Math.min(winHeight, displayBounds.height + displayBounds.y - y, winHeight - displayBounds.y + y));
mainWindow.setSize(fixedWidth, fixedHeight);
}
This version of setPosition will always resize your window so that it won't go beyond display bounds.
You can modify/extend it to allow window to go out of bounds just a bit and to scroll window's content (if needed).
I believe that with some tweaks, for most users, resizing and moving the window just slightly off the screen would look more-or-less like actually moving the window off the screen.
Here's a demo moving the window randomly using this code:
https://github.com/yoava/so-61092503
(I didn't test in with dual screens as I'm only with my laptop now.)

Improve code for WM_PAINT and WM_CTLCOLORSTATIC handlers

INTRODUCTION AND RELEVANT INFORMATION:
I have implemented complex painting of the main window’s background and its child static controls.
The picture below shows how it looks.
Static controls have SS_NOTIFY style, which is important to mention, as certain things happen when user clicks on them.
At this point, actions activated when clicking on them, are not relevant.
Both main window, and static controls, have gradient backgrounds, which were made through usage of GradientFill(...) API.
Top banner of the main window is created with gray brush, and the grid lines were created with LineTo(...) and MoveTo(...) API.
Map on the orange static control, and the top left logo are EMF files, top right logo is PNG file, and other pictures are bitmaps.
Orange static control has 4 child static controls which are owner drawn and also have SS_NOTIFY style.
It was the only way I could have thought of, which enabled me to draw the control the way it was asked of me ( if I can improve on this, please suggest it, I will accept any reasonable suggestion ).
In order to paint the orange static control, I have decided to paint its background in WM_CTLCOLORSTATIC handler, and to owner draw child static controls in a subclass procedure.
Notifications received from child static controls are also handled in the orange static controls subclass procedure, since I didn’t know how to forward them to the parent window, but are omitted since they are also irrelevant at this moment.
I have decided to provide link to the demo project, instead of making this post quite lengthy with code snippets.
I have tried to submit demo application as small and simple as it is possible.
I did not skimp on the commentaries, so I believe everything is well covered and explained in source code.
If there are still questions please leave a comment and I will reply as soon as possible ( usually immediately, or in the same day, at least ).
Here is the link to the demo project:http://www.filedropper.com/geotermistgrafika_1
Important update:
/==========================================================/
Text bellow in square brackets was the original part of the question, but is now omitted since the project had memory leaks.The above link links to an improved version.
[ Updated in response to member xMRi's comment: This link should be fine: http://www.filedropper.com/geotermistgrafika ]
/==========================================================/
I work on Windows XP, using MS Visual Studio C++ and pure Win32 API.
One note: since Express edition of VS has no resource editor, resource file and resource header were created using ResEdit from here: http://www.resedit.net/.
PROBLEM:
When I resize my window, static controls slightly flicker.
MY EFFORTS TO SOLVE PROBLEM:
I believe that my code has no memory leaks-therefore I doubt this is the problem, but being inexperienced, I would highly appreciate if my assumption can be somehow confirmed.
I think that I have properly handled WM_ERASEBKGND, and I have excluded styles CS_VREDRAW and CS_HREDRAW from my window class-therefore flickering should not be caused because of this.
I have forgot to mention, that my window has WS_CLIPCHILDREN style, so I am mentioning that now, in response to the comment bellow made by member Roger Rowland.
I have implemented double buffering for both handlers, in order to avoid flickering.
QUESTIONS:
How can I modify code in demo project to get rid of flickering?
I need advice on how to optimize both WM_PAINT and WM_CTLCOLORSTATIC handlers, so my painting code gets more efficient and faster.
A small note for second question:
I was thinking to improve my code by drawing the entire picture on the main window’s background, and to put transparent static controls on top of the part of the picture that corresponds that static controls background.
That way, I would only return NULL_BRUSH in my WM_CTLCOLORSTATIC handler, and do all the work in the WM_PAINT.
Am I on the right track with this idea? Could this work ?
Thank you.
Regards.
Firstly, your App is leaky as hell. Haven't looked for leaks, but most of them should be in WM_CTLCOLORSTATIC as you forget to delete HBITMAP's(use this neat freeware http://www.nirsoft.net/utils/gdi_handles.html to look for gdi leaks).
Secondly, your code is way to big. I noticed that you didn't use functions, maybe because you don't know what they are capable of. For example I would use:
void DrawBackground(HDC &hDC, SOMEINFOSTRUCT GradientInfo, LPCTSTR Text);
to simplify your code a lot.
Anyway enough of lecturing, let's go back to your problem. In WM_CTLCOLORSTATIC you must return brush, you want to paint background with. What you're doing now is painting background manually using Bitblt(), then return NULL brush and program paints it on your already painted background. Instead of painting it yourself, let the brush do the job.
Simply instead of the last Bitblt() use CreatePatternBrush(), but then you need to take care of this Brush and here is what you should do:
HBRUSH TempBrush = NULL; //Create global brush
//Some Code....
case WM_CTLCOLORSTATIC:
{
if (TempBrush != NULL)
{
DeleteObject(TempBrush);
TempBrush = NULL;
}
//Let's skip to the end....
GradientFill( MemDC, vertex3, 3, &gTriangle, 1,
GRADIENT_FILL_TRIANGLE );
TempBrush = CreatePatternBrush(bmp);// these 3 line should be at the
//end of every if
DeleteDC(MemDC); // or put them once outside if's
DeleteObject(bmp); // also if you delete HDC first, you don't need to
//unselect hbitmap
}
return (LRESULT)TempBrush;
}
break;
case WM_CLOSE:
{
if (TempBrush != NULL)
{
DeleteObject(TempBrush);
TempBrush = NULL;
}
//.......

C++ Win32 Owner Drawn Button's Down State

I had this working in my previous program that got lost when my hard drive crashed.
I vaguely remember how I did it, but haven't been able to find any solutions on google or msdn.
case WM_DRAWITEM: {
// pointer to items draw structure and the id
LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
int ctlResID = pDIS->CtlID;
if (ctlResID == IDC_TEST) {
// button drawn here to pDIS->hDC // this works fine
// now I want to draw the button differently if it is in its down state (holding the button down or w/e)
}
Is what I have, I think I remember one of the values in the draw item struct tells, but I cant find anything about it online.
Does anyone happen to know how to do this?
Thanks.

Create Square Window C++

Stuck on a little fiddly problem. I'm creating a GUI in C++ using XP and VS C++ using the command CreateWindow().
My question is, how do I make the inside paintable region a perfect square. When passing in the size of the window to create, some of this is deducted for the menu bar at the top, border all around etc. Are there any real time variables I can pass in, e.g. to create a 500x500 window would be:
...500+BORDER,500+MENU_TOP+BORDER...
Thanks everyone
The way I usually do it is with AdjustWindowRect. I find it simpler than the other suggested methods (which should work just as well, it's your choice). Use it as such:
RECT rect = {0, 0, desiredWidth, desiredHeight};
AdjustWindowRect(&rect, windowStyle, hasMenu);
const int realWidth = rect.right - rect.left;
const int realHeight = rect.bottom - rect.top;
And pass realWidth & realHeight to CreateWindow.
The function will, as its name suggests, adjust the window according to your window style and menu use, so that the client region matches your desired size.
you can find all the relevant size (windows framewidth, menubar height, etc) here: GetSystemMetrics(). Using these values you should be able to create a perfect square window
You can get all the UI metrics from the GetSystemMetrics() API call.
For example, the menu will be SM_CXMENU and SM_CYMENU.

Manually drawing gradients for buttons, toolbars, tabs etc?

I would like to update some toolbar-like code we have to have a Vista/Win7 gradient roundedness to them.
Currently, the buttons have the Windows 2000 look & feel: blocky, single-tone.
I've played around with the XP themes, and using DrawThemeBackground, DrawThemeEdge, etc.; but I'm very dissatisfied with the theme drawing mechanics (the buttons are large, and the theme draws them as 2-tone, top half and bottom half, which looks okay when the buttons are small - it gives them a halfway decent appearance of being a gradient or having a rounded quality to them. But as large as these buttons are, they look stupid.
Experimenting by simply observing how many of the controls are drawn in various apps and controls, I can see that most of them seem to use gradients - where the top of the control appears a light color and fades to the bottom to a darker color - OR - where it is a lighter color than the background at the top, increases towards near-white at the middle, then fades back to a darker color towards the bottom.
I'm not really sure where to go from here. DrawThemeXXX seem to be inadequate. I don't really want to replace the entire control with a new one that has improved drawing but would require that I swap out some of the code for how the current custom control works, and risk various problems with some other library. I'd rather just have a way to draw arbitrary objects in the style of the current version of Windows that I'm running on. I would have thought that the theme drawing functions would have covered this. But they're fairly brain damaged, as I described.
Can someone point me towards 'How are modern C++ applications supposed to draw custom GUI elements so that they might reasonably expect a graceful appearance under XP, Vista, and Windows 7?'
We use MFC, Gdiplus, and raw Win32 APIs in our code, currently.
Here's to hoping someone knows a great deal about drawing modern GUIs under Windows from C++!
Just so that this isn't a wall of text, here's the current version of the paint handler, which draws the button with an etched border when 'hot-tracking' and both an etched border and the icon + text "depressed" (shifted by 1,1) when in a pressed state:
void CPlacesButton::PaintButton(CDC & dc, CRect & rcClient)
{
const int kLabelHeight = 8;
COLORREF clrHiLt = GetSysColor(COLOR_BTNHIGHLIGHT);
COLORREF clrShdo = GetSysColor(COLOR_BTNSHADOW);
COLORREF clrText = GetSysColor(COLOR_BTNTEXT);
COLORREF clrFace = GetSysColor(COLOR_BTNFACE);
// draw the button's background & border
if (m_bPressed || m_bDrawPressed || m_bMouseOnButton)
{
COLORREF clrDarkened = Darken(clrFace, -0.01f);
dc.FillRect(rcClient, &CBrush(clrDarkened));
//dc.Draw3dRect(rcClient, clrShdo, clrHiLt);
//dc.RoundRect(&rcClient, CPoint(10,10));
dc.DrawEdge(&rcClient, EDGE_ETCHED, BF_RECT|BF_FLAT);
//dc.DrawFrameControl(&rcClient, DFC_BUTTON, DFCS_BUTTONPUSH|DFCS_PUSHED);
}
// else if (m_bMouseOnButton) // hot draw
// //dc.Draw3dRect(rcClient, clrShdo, clrHiLt);
// dc.DrawEdge(&rcClient, EDGE_ETCHED, BF_RECT);
// //dc.RoundRect(&rcClient, CPoint(10,10));
else
dc.FillRect(rcClient, &CBrush(clrFace));
// use transparent mode for everything that follows
dc.SetBkMode(TRANSPARENT);
// center icon
CPoint ptIcon((rcClient.Width() - m_nIconSize) / 2, ((rcClient.Height() - m_nIconSize) / 2) - kLabelHeight);
if (m_bPressed || m_bDrawPressed)
ptIcon.Offset(1, 1);
// determine the state to draw ourselves in
const UINT nState = DST_ICON | (IsEnabled() ? DSS_NORMAL : DSS_DISABLED);
// draw our icon
dc.DrawState(ptIcon, CSize(m_nIconSize, m_nIconSize), m_hIcon, nState, (HBRUSH)NULL);
// create & select the font to use for the button's label
CFont guiFont;
VERIFY(guiFont.CreateStockObject(DEFAULT_GUI_FONT));
AutoSelectGDIObject select_font(dc, guiFont);
// determine clipping rect for label
CRect rcText(0, ptIcon.y+m_nIconSize+kLabelHeight, rcClient.Width(), ptIcon.y+m_nIconSize+kLabelHeight);
rcText.InflateRect(0,20);
if (m_bPressed || m_bDrawPressed)
rcText.OffsetRect(1, 1);
dc.SetTextColor(clrText);
if (IsEnabled())
dc.DrawText(m_strCaption, rcText, DT_VCENTER|DT_SINGLELINE|DT_CENTER);
else
dc.GrayString(NULL, NULL, (LPARAM)(LPCTSTR)m_strCaption, 0, rcText.TopLeft().x, rcText.TopLeft().y, rcText.Width(), rcText.Height());
}
I left some of the commented out variations in the code to indicate some hints as to what other possibilities I've tried. However, they're just a hint, as the complete alternate examples are not present.
Actually duplicating the look of the various flavors of Windows is ridiculously difficult, especially if your app can run on more than one version of windows.
I think that they intended to give you the api's to do this back in the Win2k/Win95 days, but then WinXP came along with shading and overlays, and the old API was completely inadequate.
So they came up with the theme stuff, which isn't really even an API so much as an API and a set of graphical primitives all jammed together. But they didn't follow through and allow the set of graphical primitives to be extended or replaced, so themes only works when your controls are a close match to the standard set.
So, for Win9x/Win2k. You use
DrawFrameControl
DrawEdge
For WinXP
DrawTheme
For WinVista/7
DrawTheme
DwmXXX functions
GradientFill ??
Now I suspect that Windows isn't actually using GradientDraw. I suspect it's actually using some DX10 shaders that are built in to the window manager code, but I don't know how to get at that, s I've been using GradientDraw instead. This code will give you a linear fade from the top of the control to the bottom.
INLINE void SetTrivertex(TRIVERTEX & vtx, int x, int y, COLORREF cr)
{
vtx.x = x;
vtx.y = y;
vtx.Red = (SHORT)(GetRValue(cr) * 256L);
vtx.Green = (SHORT)(GetGValue(cr) * 256L);
vtx.Blue = (SHORT)(GetBValue(cr) * 256L);
vtx.Alpha = (SHORT)(255 * 256L);
}
...
// fill the interior from the top down with a gradient that starts at crTop
// and ends with the crBottom
TRIVERTEX vtx[2];
SetTrivertex (vtx[0], prc->left+1, prc->top+1, crTop);
SetTrivertex (vtx[1], prc->right-1, prc->bottom-1, crBottom);
GRADIENT_RECT gRect = { 0, 1 };
GradientFill(hdc, vtx, 2, &gRect, 1, GRADIENT_FILL_RECT_V);
You never mentioned the MFC Feature Pack. Have you taken a look at it yet? Download for VS2008, included with VS2008 SP1. The CDrawingManager has lots of special effects. It has great support for application themes.
MFC alone isn't exactly skinning friendly. Apart from using another GUI (Qt is great for custom skinning) you can look at solutions like SkinCrafter.