How to limit frame resizing based on view's size? MFC doc/view architecture - visual-c++

MFC doc/view architecture, sdi (more precisely multiple top-level windows).
In my view class, I set my "playground" (i.e. logical space) with SetScrollSizes(); Then I want to limit maximum frame window size to that of view's maximum size.
Here is what I'm doing but I think there might be better solution, please advice:
I'm implementing OnGetMinMaxInfo() in my CMainFrame. There I try to get active view's scroll sizes, and set lpMMI->ptMaxTrackSize appropriately. Below is the code:
void CMainFrame::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
// Call base version:
CFrameWndEx::OnGetMinMaxInfo(lpMMI);
// Get active view:
CScrollView *pScrollView = (CScrollView *)GetActiveView();
if (pScrollView && pScrollView->IsKindOf(RUNTIME_CLASS(CMyFckinView)))
{
// Get total size of playground:
CSize sizePlayground = pScrollView->GetTotalSize();
// Test if the size is non-zero, i.e. there is at least one node displayed:
if (sizePlayground.cx && sizePlayground.cy/* && !IsPrintPreview()*/)
{
// Set maximum window size to match our playground size:
CRect rectClient, rectWindow;
pScrollView->GetClientRect(&rectClient);
this->GetWindowRect(&rectWindow);
if (rectWindow.top > -5000 && rectWindow.left > -5000) // Avoid when minimized...
{
lpMMI->ptMaxTrackSize.x = sizePlayground.cx + (rectWindow.Width() - rectClient.Width());
lpMMI->ptMaxTrackSize.y = sizePlayground.cy + (rectWindow.Height() - rectClient.Height());
return;
}
}
}
}
This works but has one problem: When print preview is displayed (standard MFC print preview), I obviously want to allow free window resizing, so I use runtime info GetActiveView()->IsKindOf(...) to determine that active view is really my view, and not print-preview's view (which is CPreviewViewEx). But when I close the print preview, OnGetMinMaxInfo is not called, so I'm unable to adjust frame size according to my view again. As soon as I move the window OnGetMinMaxInfo gets called again and correctly adjusts frame size, but without manually moving the window old size (to which the print preview was sized to) is retained and has ugly artifact.
What can I do? Basically if I could trap the moment when print preview is closed, I could use following trick:
// Trigger the WM_MINMAXINFO message:
CFrameWnd *pFrame = GetParentFrame();
RECT rectWindow;
pFrame->GetWindowRect(&rectWindow);
pFrame->MoveWindow(&rectWindow);
But I don't know how to trap print-preview closure.
What I'm trying to accomplish seems quite standard: who would want to have frame window resized bigger than view's logical size (set by SetScrollSizes())? So there should be some more natural solution maybe?

In your CMyFckinView, handle a message that is reliably sent when the print preview is closed and then post a user message to the mainframe which will trigger your "force minmax" code. Perhaps WM_FOCUS or WM_ACTIVATE?

Related

Using CMFCMenuButton::SizeToContent does not seem to work as I would like. Why?

I am perplexed about the SizeToContent method of the CMFCMenuButton control.
This is my dialog in the IDE:
As you can see, I have specifically made the button wider than the two on the far right.
I added the following code to OnInitDialog:
// Resize (if required)
const auto sizNewButton = m_btnReset.SizeToContent(true);
CRect rctButton;
m_btnReset.GetWindowRect(&rctButton);
if(sizNewButton.cx > rctButton.Width())
{
m_btnReset.SizeToContent();
}
Yet, when I run my application in English:
It has made it smaller. My application supports 50+ languages by using satellite DLLs and I was hoping to only resize to content if it was required. But it seems to resize it anyway. Have I missed a step here?
I have checked the properties for the control in the IDE and it is not set to auto resize:
I notice that the help documentation states:
The new size of the button is calculated to fit the button text, image, and arrow. The framework also adds in predefined margins of 10 pixels for the horizontal edge and 5 pixels for the vertical edge.
I had a look at my button:
Default size: 48 x 23 (the GeWindowRect result).
Calculated size: 57 x 23 (the SizeToContent result).
If I adjusted my code like this:
if((sizNewButton.cx - 10) > rctButton.Width())
That would bring it down to 47 and thus would not resize. I am assuming the code is not working right because of the padded margin that GetWindowRect knows nothing about.
Searched it, and found that the problem is MFC's CMFCMenuButton::SizeToContent() implementation in afxmenubutton.cpp:
CSize CMFCMenuButton::SizeToContent(BOOL bCalcOnly)
{
CSize size = CMFCButton::SizeToContent(FALSE); // <==== The culprit!!!
size.cx += CMenuImages::Size().cx;
if (!bCalcOnly)
{
SetWindowPos(NULL, -1, -1, size.cx, size.cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
}
return size;
}
That is, it calls the base implementation of SizeToContent() with the bCalcOnly parameter set to FALSE, which means it will also resize the control to just fit the text (without the drop-down arrow). This is less than required for the text plus the arrow, and of course the original size is lost.
A workaround can be get the (original) width, before the SizeToContent() call, and work with this instead of the new one:
CRect rctButton;
m_btnReset.GetWindowRect(&rctButton);
const auto nOrigWidth = rctButton.Width(); // Store the original width
const auto sizNewButton = m_btnReset.SizeToContent(true); // This resizes the control!!!
if (sizNewButton.cx > nOrigWidth) // Compare to the original width rather than the new one
m_btnReset.SizeToContent();
else // Restore original width
m_btnReset.SetWindowPos(NULL, -1, -1, nOrigWidth, sizNewButton.cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
Alternative Workaround:
Define a new CMFCMenuButton-based class, overriding SizeToContent() - in the implementation call the base CMFCButton::SizeToContent() with the bCalcOnly parameter passed by the caller, not with FALSE. Map the control to this class instead of CMFCMenuButton. That is use a class that fixes it. Too much of an overkill for just a workaround though.

Change visible property sometimes change the center position of the view (possible bug?)

I've 3 (loader, locker and debug view) hidden views (touchEnabled and visible set to false, and zIndex to 1) above the main view (zIndex = 2).
Each 'over' view has this method:
$.debugView.show = function() {
$.debugView.touchEnabled = $.debugView.visible = true;
$.debugView.zIndex = 3;
};
$.debugView.hide = function() {
$.debugView.touchEnabled = $.debugView.visible = false;
$.debugView.zIndex = 1;
};
This screen has the 3 'over' views hidden:
Now, I'm opening the 'debug view', but, SOMETIMES it seems like it changes the positions (as if the center it's on the top left corner instead of the center of the device).
Instead of the required result:
If I use the opacity instead of the visible property, it works properly.
This might be an SDK bug right?
<Alloy>
<Window>
<View id="content"/>
<View id="locker"/>
<View id="loader"/>
<View id="debugView"/>
</Window>
</Alloy>
All of these 4 views don't have width or height (so it uses the Ti.UI.FILL as default)
I have noticed this too with a completely different implementation. I had just one view that I included in a window.
Apparently the left and top calculations were not done properly if the elements is hidden.
What I did to solve the issue is to hardcode the left/top position by calculating the left position using this:
$.content.left = (Ti.Platform.displayCaps.platformWidth - 75) / 2;
Where in my case 75 is the width the element has, so that'll be bigger in your case. You can do the same for height.
Now, this is an iOS only solution. On Android you will need to take DPI into consideration calculating it.
I do think it is a bug, though this solution works perfectly for me. I recommend looking at JIRA and see if it is a known issue, and if not, raise it with a very specific explanation of the problem, preferably with a reproducible case delivered as an app. Classic would help most. And if it is not reproducible in classic it might be an alloy issue.

x11 - how to set _NET_WM_ALLOWED_ACTIONS

I use XCreateSimpleWindow to create x11 window. xprop shows following allowed actions for my window:
_NET_WM_ALLOWED_ACTIONS(ATOM) = _NET_WM_ACTION_MOVE, _NET_WM_ACTION_RESIZE, _NET_WM_ACTION_STICK, _NET_WM_ACTION_MINIMIZE, _NET_WM_ACTION_MAXIMIZE_HORZ, _NET_WM_ACTION_MAXIMIZE_VERT, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_CLOSE, _NET_WM_ACTION_SHADE, _NET_WM_ACTION_CHANGE_DESKTOP, _NET_WM_ACTION_ABOVE, _NET_WM_ACTION_BELOW
What happens if I don't set them explicitly (like above) ? A window has a default list which contains all of them ? How to set them explicitly ?
Edit1
Here is example code which sets only one allowed action:
Atom aa = XInternAtom(d, "_NET_WM_ALLOWED_ACTIONS", False);
Atom close = XInternAtom(d, " _NET_WM_ACTION_CLOSE", False);
XChangeProperty(d, w, aa, XA_ATOM, 32, PropertyNewValue, (unsigned char*)&close, 1);
Window manager still let me move or resize window so maybe I should send some client message ? I want to have a window which allows only for close actions.
1) no, by default window does not have properties, but window managers often set some default values. Try to run your program without WM to see difference
2) use "ChangeProperty" request. Window property is some data associated with window + a little bit of metadata: name (atom) and type (atom). If size of data is more than single type would need it's assumed you have array of those. For example, atom is just 32 bit unsigned int. If you see 8 bytes property of type atom, you interpret content as two atoms. See XChangeProperty documentation if you are using xlib

GTK3 GtkLayout with cairo, cannot get update region

I am trying to draw a GtkLayout using cairo. The layout is huge and I need to get the part that is visible in the container window and update that part only. With GTK2 the expose event data was sufficient to do this. I am not successful with GTK3.
In the function to handle "draw" events, I did the following:
GdkWindow *gdkwin; // window to draw
cairo_region_t *cregion; // update regions
cairo_rectangle_int_t crect; // enclosing rectangle
gdkwin = gtk_layout_get_bin_window(GTK_LAYOUT(layout));
cregion = gdk_window_get_update_area(gdkwin);
cairo_region_get_extents(cregion,&crect);
expy1 = crect.y; // top of update area
expy2 = expy1 + crect.height; // bottom of update area
The problem is that cregion has garbage. Either gdk_window_get_update_area() is buggy or I am not using the right drawing window.
Passing the GtkLayout as follows also does not work (this is the function arg for g_signal_connect):
void draw_function(GtkWidget *layout, cairo_t *cr, void *userdata)
Whatever gets passed is not the GtkLayout from g_signal_connect, but something else.
================= UPDATE ====================
I found a way to do what I want without using GtkLayout.
I am using a GtkDrawingArea inside a viewport.
I can scroll to any window within the large graphic layout
and update that window only. Works well once I figured out
the cryptic docs.
scrwing = gtk_scrolled_window_new(0,0);
gtk_container_add(GTK_CONTAINER(vboxx),scrwing);
drwing = gtk_drawing_area_new();
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrwing),drwing);
gtk_scrolled_window_set_policy(SCROLLWIN(scrwing),ALWAYS,ALWAYS);
scrollbar = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrwing));

How to avoid CListCtrl items to be partially visible?

I have a resizeable CListCtrl and I want to avoid any item being displayed partially, ever.
For example:
I want Item 9 to not be displayed in this case. Is there a flag or method for this? How would you go about solving this issue?
I tried the following and it was no good:
void CMyCListCtrl::OnEndScrolling()
{
int iCount = this->GetCountPerPage();
EnsureVisible(iCount - 1, FALSE);
}
after catching
...
ON_NOTIFY( LVN_ENDSCROLL, IDC_LIST1, OnEndScroll )
...
void CWheelTestDlg::OnEndScroll(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMLVSCROLL pnmLVScroll = (LPNMLVSCROLL) pNMHDR;
m_MyListCtrl.OnEndScrolling();
*pResult = 0;
}
In the CListCtrl parent dialog. (which I don't want to do, I want to do everything in my CListCtrl derived class only, if possible).
All I accomplish is showing item 9 completely, but item 10 is partially visible below it. If I have 30 items I don't want to scroll the list to show item 30, I want to show up to item 8 with no partially visible item below it.
CListCtrl doesn't appear to support Integral Height.
Here's a solution that accomplishes what you desire by forcefully changing the control height [with commented conditions] (http://www.codeproject.com/Messages/418084/Socket-accept-call.aspx):
/////////////////////////////////////////////////////////////////////////////////
// This assumes a REPORT-style CListCtrl.
//
// Resize the control. This works correctly only if scrolling is disabled. If
// there is scrolling, then setting to the size from ApproximateViewRect() will
// always give scroll bars showing. Which is irritating.
//
// We need to adjust the vertical size from what ApproximateViewRect() returns
// by one row minus border width
//////////////////////////////////////////////////////////////////////////////////
CSize sz = m_list.ApproximateViewRect(); // always adds room for a new row
CRect itRect; // Get the height of a single row (there had better *be* a row!)
m_list.GetItemRect(0, &itRect, LVIR_BOUNDS);
int vOffset = itRect.Height() - 3; // leave a little 'cuz it looks better
m_list.SetWindowPos(NULL, 0, 0, sz.cx, sz.cy - vOffset,
SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE);
I have the similar problem in wince, and find a solution accidentally. No direct solution in internet, so i decide to re-position scroll bar after receive some message, and the only message i can used in wince is WM_LBUTTONDOWN, other messages such as OnEndScroll are not called, maybe something wrong in my code.
Whatever, i use Timer(ON_WM_TIMER) to re-position scroll bar when receive WM_LBUTTONDOWN message, then find that list control doesn't scroll automatically! then i remain a empty OnTimer function and remove everything else. It works, and i guess list control use Timer to scroll partial row.
Hope useful to you.

Resources