resizing rect but keep text at same pos - c++

I have a rect that i resize with text on it.
The text is drawn on top of that rect with DT_CENTER, I want the text to be able to stay in its same position but only print the parts that would be seen where the rect is ontop.
At the moment my text just prints at its right spot but shows when the rect is not even under the text.
This is set in WM_TIMER i add 10 to i everytime timer called
this it is all drawn in WM_PAINT
This is when the rect slides from left to right:
if (i <= m_sWndSize.cx)
{
m_rcCurrent = { 0, 0, i, m_sWndSize.cy };
m_rcCurrentText = { 0, 0, i, m_sWndSize.cy };
}

Worked it out with what Jonathan Potter said.
With the use of CreateRectRgnIndirect and SelectClipRgn
Thanks

Related

MFC DrawText, vertical, DT_CALCRECT with lf_escapement = 900

I'm working on a MFC project with some GDI drawings.
I use DC.DrawText to draw a vertical text into a DC using a LOGFONT with lfEscapement = 900.
The text is output when i use DT_NOCLIP in the desired vertical formatting.
However to center this text i used a call to DC.DrawText with the DT_CALCRECT argument.
I recognized that, despite the text is indeed drawn vertically, the CRect has a larger width
than height.
My intuition says me that a vertical drawn text should have a larger height than width.
I did not include the calculation for centering the text. The question is just about what i can rely upon when i implement that vertical centering.
Does DC.DrawText with DT_CALCRECT ignore escapement?
void CMFCFontTestDlg::OnPaint()
{
CPaintDC dc(this); // Gerätekontext zum Zeichnen
if (IsIconic())
{
...
}
else
{
CDialogEx::OnPaint();
CRect clTextRect;
CFont myFont;
myFont.CreateFont(12, 0, 900, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _T("Tahoma"));
CFont* oldFont = dc.SelectObject(&myFont);
dc.DrawText(_T("000000"), clTextRect, DT_CALCRECT);
clTextRect.MoveToXY(100, 100);
dc.DrawText(_T("000000"), clTextRect, DT_NOCLIP);
dc.SelectObject(oldFont);
}
}
I found out about it in the remarks to the DrawTextEx function
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-drawtextexa.
However, please note that neither the documentation of DrawText does say anything about this nor the documentation of the DT_CALCRECT flag.
This is likely to be overseen if one uses DrawText and not DrawTextEx.
I informed MS about this via "Is this page helpful?" feedback possibility.
Remarks
The DrawTextEx function supports only fonts whose escapement and
orientation are both zero.
The text alignment mode for the device context must include the
TA_LEFT, TA_TOP, and TA_NOUPDATECP flags.
Considering this the solution is to use some trigonometric calculation starting from the size determined for escapement = 0 and then calculate the topleft and bottomright points of the rotated rect.

Why is hovering over a static Win32 control increasing memory and removing my GUI?

Windows API resources can cause memory leaks if not taken care of properly. Whether that's the case or not in this issue, I'm assuming it's related. Although I show how I pinned down to where the issue is coming from, I haven't been able to solve it.
I have two types of static class controls using the Win32 API, which is abstracted in my classes:
Label
LinkLabel 
The issue: Whenever I add these two controls, Visual Studio 2017's Diagnostic Tools shows Process Memory (MB) increasing from 3MB to 11MB when I have either both setFont() or setHoverColor() lines enabled, and eventually everything in my GUI space disappears -- gone from existence, like some well-known bookstores.
This code is fine (3MB stays the same constant rate in Process Memory):
// Linked Label
myLinkLabel.init("http://www.google.com", 50, 450);
myLinkLabel.setColor(0, 0, 255);
myLinkLabel.onClick(lbl_Click);
myLinkLabel.setFont("Arial", 40, true);
//lbl.setHoverColor(255, 0, 0); 
// label
myLabel.init("A regular static label", 0, 0);
myLabel.setColor(0, 255, 0);
myLabel.setFont("Arial", 40);
//myLabel.setHoverColor(255, 0, 0);
This next code uncomments the last line. After hovering over myLabel, and the red highlight color appears, Process Memory's 3MB increases to 7MB+. It sits for a bit, then goes up to 9MB+. So, something is wrong in it. 
// Linked Label
myLinkLabel.init("http://www.google.com", 50, 450);
myLinkLabel.setColor(0, 0, 255);
myLinkLabel.onClick(lbl_Click);
myLinkLabel.setFont("Arial", 40, true);
//lbl.setHoverColor(255, 0, 0); 
// label
myLabel.init("A regular static label", 0, 0);
myLabel.setColor(0, 255, 0);
myLabel.setFont("Arial", 48);
myLabel.setHoverColor(255, 0, 0);
So, let's dig into my setHoverColor():
void Label::setHoverColor(const BYTE red, const BYTE blue, const BYTE green)
{
 m_hoverColorEnabled = true;
 m_hoverColor = RGB(red, green, blue);
}
Okay, nothing too amazing in the code above. This tells me to look in WndProc. 
The events this static control uses is WM_SETCURSOR. 
case WM_SETCURSOR:
{
HWND m_handle = (HWND)wParam;
// Label
for (int i = 0; i < frm.getLabelControlCount(); i++)
{
if (frm.getLabelControl(i).getHandle() == m_handle)
{
if (frm.getLinkLabelControl(i).isLink())
{
// Set hover color to link 
if (frm.getLabelControl(i).isHoverColorEnabled())
frm.getLabelControl(i).setColor(frm.getLabelControl(i).getHoverColor());
// Update cursor to hand
SetClassLongPtr(frm.getLabelControl(i).getHandle(), GCLP_HCURSOR, (LONG_PTR)frm.getLabelControl(i).getHoverCursor());
}
}
else
{
// Set link to blue and use default arrow 
if (frm.getLabelControl(i).isHoverColorEnabled())
frm.getLabelControl(i).setColor(0, 0, 255);
SetClassLongPtr(frm.getLabelControl(i).getHandle(), GCLP_HCURSOR,
(LONG_PTR)LoadCursor(NULL, IDC_ARROW));
}
}
 
When commenting this section of code, Process Memory stays constant at 3MB. When uncommenting this section, Process Memory increases. So, this is the main code that's causing the problem apparently. 
This section of code is basically updating the label's text color based on its current mouse hovering state. It's blue when not hovered over, and it's red when hovered over.
setColor() is the following code:
void Label::setColor(const COLORREF color)
{
 m_foreColor = color;
 setFont(m_fontName, m_fontSize, m_bold, m_italic, m_underlined);
}
Which is also calling setFont() to update it:
bool Label::setFont(const std::string &fontName, const int size, const bool bold,
 const bool italic, const bool underlined)
{ 
 DWORD dwItalic;
 DWORD dwBold;
 DWORD dwUnderlined;
 SIZE linkSize;
 dwItalic = (italic) ? TRUE : FALSE;
 dwBold = (bold) ? FW_BOLD : FW_DONTCARE;
 dwUnderlined = (underlined) ? TRUE : FALSE;
 m_font = CreateFont(size, 0, 0, 0, dwBold, dwItalic, dwUnderlined, FALSE,
  ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
  DEFAULT_PITCH | FF_SWISS, fontName.c_str());
 SendMessage(m_handle, WM_SETFONT, WPARAM(m_font), TRUE);
 
 // Calculate the correct width and height size 
 HDC hDC = GetDC(m_handle);
 SelectFont(hDC, m_font);
 GetTextExtentPoint32(hDC, m_text.c_str(), (int) m_text.length(), &linkSize);
 setSize(linkSize.cx, size); 
 
 // Store font information
 m_fontName = fontName;
 m_fontSize = size;
 m_bold = bold;
 m_underlined = underlined;
 m_italic = italic;
 
 return true;
}
My guess is this is a lot of updating for creating a font and re-creating it based on every hover. My reasoning was it wouldn't update the font color unless setting the font again. Although I see room for this in the near future, am I forgetting to delete a resource or something here? Any thoughts are welcomed. Pretty sure this will solve LinkLabel as well. 
Your basic problem is that you keep generating new fonts and never releasing the old ones.
Each time setfont is called you allocate and select a new font. But when you select the NEW font into the HDC you never clean up the old font.
SelectFont returns the previously selected font which you need (unless it is a stock font) to do a DeleteFont on.
Additionally you have a bigger resource leak on the GetDC call - the MS documentation for getDC suggests that you use a releaseDC when you have completed the usage.
As far as I understand it is NOT required to reset the font just to reset the color.

How to draw separate texts one by one in MFC?

I am trying to draw two pieces of text one by one in MFC as they would be part of the same text. Right now I am drawing them as they are just one string:
CString text1 = "A text";
CString text2 = "A second text";
CString textToDraw = text1 + text2;
CDC* dc = GetDC(); //assume that this is initialized elsewhere
dc->TextOut(0, 0, textToDraw);
It is simple to draw the both texts as one because I only need to find the position where they should be started to be draw. The problem I am facing is how to compute the new X coordinate at which the second text should be draw (considering that the texts can be chosen at run-time so they do not have a known length):
dc->TextOut(0, 0, text1);
int X;
//how should I compute X...?
dc->TextOut(X, 0, text2);
I appreciate any help received!
You can use DrawText() with DT_CALCRECT flag to calculate the width and height the text would occupy without actually drawing the text. The following may be the answer to your question.
dc->TextOut(100, 100, text1);
RECT rect = { 0, 0, 0, 0 };
dc->DrawText(text1, &rect, DT_CALCRECT);
dc->TextOut(100 + rect.right, 100, text2);

Borderless window with Aero Snap too large in maximized state

I am trying to make a borderless window in Qt5.6.0, with aero-snap functionality.
Everything works, except when I maximize the window : it is too big.
My screen resolution is 2560x1440, so the window should be sized 2560x1400 (40 Pixels for the Taskbar), but in the WM_SIZE message, the new size is 2576x1416.
So the window is exactly 8 pixels too big in every direction.
This also means that the window is not aligned in the top-left corner, it is exactly 8 pixels off-screen in both directions.
I can't find a solution for this problem, everything I have tried doesn't work and causes bugs.
The only thing that fixes this is to remove the WS_CAPTION and WS_THICKFRAME styles, but then I lose the areo snap functionality.
I somehow have to tell Qt or DWM to make the window 16 pixels smaller and move it 8 pixels right, and bottom. Does anybody have an idea on how to do that?
I somehow have to tell Qt or DWM to make the window 16 pixels smaller
and move it 8 pixels right, and bottom. Does anybody have an idea on
how to do that?
DWM is Desktop Window Manager? Then the platform is Windows then.
As long as it is about Qt 5.6 and you very likely talking about the widget with Qt::CustomizeWindowHint attribute set then there is a known bug in Qt which is not fixed yet:
https://bugreports.qt.io/browse/QTBUG-4362
I stumbled upon that bug a couple of times and the workaround proposed by BiTOk at the link above worked for me.
My first try, was setting the window geometry to the available geometry:
QRect rect = QApplication::desktop()->availableGeometry();
setGeometry(rect.left() , rect.top(), rect.right(), rect.bottom());
The only Problem is that the window is a pixel too small on the right and bottom side and
setGeometry(rect.left() , rect.top(), rect.right() + 1, rect.bottom() + 1);
gives me an error:
QWindowsWindow::setGeometry: Unable to set geometry 2560x1400+0+0 on QWidgetWindow/'MainWindowWindow'. Resulting geometry: 2576x1416+-8+-8 (frame: 0, 0, 0, 0, custom margin: 0, 0, 0, 0, minimum size: 45x13, maximum size: 16777215x16777215)
Then I looked at the rectangle coordinates of Visual Studio 2015 and they are the same size as my implementation of a borderless window, 8 pixels larger in every direction.
I can give the contents of my window a margin of 8 so it doesn't clip out of the screen if the window is maximized and set the window region:
setContentsMargins({ 8, 8, 8, 8 });
HRGN WinRgn;
RECT winrect;
GetClientRect(hwnd, &winrect);
WinRgn = CreateRectRgn(8, 8, winrect.right - 8, winrect.bottom - 8);
SetWindowRgn(hwnd, WinRgn, true);
When the window gets restored, we need to reset the previous changes.
The result is:
case WM_SIZE:
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hwnd, &wp);
if (wp.showCmd == SW_MAXIMIZE) {
setContentsMargins({ 8, 8, 8, 8 });
HRGN WinRgn;
RECT winrect;
GetClientRect(hwnd, &winrect);
WinRgn = CreateRectRgn(8, 8, winrect.right - 8, winrect.bottom - 8);
SetWindowRgn(hwnd, WinRgn, true);
UpdateWindow(hwnd);
is_fullscreen = true;
} else {
if (is_fullscreen) {
setContentsMargins({ 0, 0, 0, 0 });
SetWindowRgn(hwnd, NULL, true);
is_fullscreen = false;
}
}
break;
Other posts have already answered the question, but I would just like to add that it might be a good idea to use GetSystemMetrics rather than a hard-coded value of 8.
Example
#include <Windows.h>
void MyWindow::changeEvent(QEvent* ev) {
if (ev->type() == QEvent::WindowStateChange) {
const auto state = windowState();
if(state & Qt::WindowMaximized) {
const int x = GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
const int y = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
setContentsMargins({x, y, x, y});
}
else {
setContentsMargins({0, 0, 0, 0});
}
}

Clear GDI shape inside the loop

I have a program which draw a Rectangle under mouse cursor and show the pixel color, but I can't manage it to clear the shape inside the while loop, if I use 'InvalidateRect()' it clear rectangle too fast and flickering, if not use 'InvalidateRect()' then Rectangle keep duplicating like THIS, how to fix that?
HWND hwnd;
POINT p;
unsigned short R=0, G=0, B=0;
void drawRect()
{
GetCursorPos(&p);
HDC hdc = GetDC(NULL);
HPEN border = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
HBRUSH background = CreateSolidBrush(RGB(R, G, B));
SelectObject(hdc, border);
SelectObject(hdc, background);
Rectangle(hdc, p.x+10, p.y+10, p.x+40, p.y+40);
DeleteObject(border);
DeleteObject(background);
}
void init()
{
while (GetAsyncKeyState(VK_RBUTTON) & 0x8000)
{
grabPixel(); //get RGB color from cursor coordination
drawRect(); //draw preview rectangle under cursor
InvalidateRect(hwnd, NULL, true);
}
}
Note: it doesn't have WinMain() or WndProc()
There are all sorts of things wrong with this. What are you actually trying to do?
From the fact that you're using GetDC(NULL), it looks like this is supposed to be drawing a rectangle on the entire screen.
Where is the hwnd value coming from? If that window does have a message loop (and it probably does), then that's the window being invalidated and redrawing itself.
A note: InvalidateRect merely marks the rectangle as needing-to-be-painted the next time that that application's (actually thread's, more-or-less) message queue is empty. UpdateWindow will cause a WM_PAINT message to be sent immediately.
drawRect isn't cleaning up properly, either. It should call ReleaseDC when it's finished, and it ought to restore the previous drawing objects after it's finished (and most definitely before it deletes them) as well:
HBRUSH oldBackground = SelectObject(hDC, background);
// ...
SelectObject(hDC, oldBackground);
What you probably want to do is, when selection starts, create a window the size of the screen and copy the existing screen into it. Then you can draw all over that intelligently.
The DrawDragRect function (see my blog) is designed for this sort of thing.