Different layout between Mac and Windows/Linux (same *.ui files) - c++

I'm deploying my application with Qt Framework.
The behaviour of layout is equal in Windows and Linux; and if I resize the windows the layout adapts. This doesn't happen in Mac OS. To develop for Mac, I'm using a virtual machine (the resolution is 1024x768), but, when I try to start my bundle in a "true" Mac (resolution's biggest) the result is terrible!
I put some pictures:
MAC - virtual machine (1024x768) - First tab (OK)
MAC - virtual machine (1024x768) - Second tab (Not OK)**
**The second tab "Rubrica Contatti" is designed like the first tab "Ricettario". In designer the second tab seems ok, at runtime no!
I have tried to create a new project for test.
I've set the Central Widget with Form Layout.
I've created 2 Widgets that contain these items (each widget has a Grid Layout):
LineEdit
ComboBox
GroupBox (with Grid Layout) containing 2 labels and 2 checkboxes
All these three items are clustered in vertical layout. I put the left widget in QSizePolicy::Minimum and the right one in QSizePolicy::Preferred. In Qt Designer the left panel is smaller and the right one is horizontally enlarged. When I start the app, both widget have the same width and are in the center of the window.
Note:
When I open any project, I get this text in "General Messages" .
Warnings while parsing QML type information of /Users/denny/Qt/5.5/clang_64/qml:
<dump of /Users/denny/Qt/5.5/clang_64/qml>:1:24: Reading only version 1.1 parts.
<dump of /Users/denny/Qt/5.5/clang_64/qml>:10:5: Expected only Component and ModuleApi objects definitions.
Could it be the cause?

The problem remains, but it solved so (for now)...
QApplication::setStyle("fusion");
My app won't use Mac Themes, but it will use Fusion Theme...
Note:
The graphic is uglier, but it works!

For the panel on the left, set the horizontal size policies to
QSizePolicy::Minimum. It means this widget can be larger but there is no point expanding it.
For the panel on the right, set the horizontal size policies to
QSizePolicy::Preferred. It means if there is space to grow further use it.
Put the left panel widget in a single widget, the right panel widgets in another widget, and use a form layout. The form layout usually set the right size policies for left and right.

It happens because, different styles use different expanding policies.
For example the QFormLayout deals with enum QFormLayout::FieldGrowthPolicy.
QFormLayout::FieldsStayAtSizeHint - This is the default for QMacStyle.
QFormLayout::AllNonFixedFieldsGrow - This is the default policy for most styles.
QFormLayout::ExpandingFieldsGrow - This is the default policy for Plastique.

Related

Setting Style Sheet for a Check Box Qt removes Check Box from GUI QT

For my Qt Dialog Window, I am trying to create a checkbox that is centered, but is always checked and uncheckable.
I did this by combining
ui->baseBox->setStyleSheet("margin-left:50%; margin-right:50%;");
and
ui->baseBox->setAttribute(Qt::WA_TransparentForMouseEvents);
ui->baseBox->setFocusPolicy(Qt::NoFocus);
However, I can only do the first bit of code or the second. When I run the both of them together, my checkBox baseBox completely disappears from the GUI.
So I tried doing :
ui->baseBox->setAttribute(Qt::WA_TransparentForMouseEvents);
ui->baseBox->setFocusPolicy(Qt::NoFocus);
ui->baseBox->setStyleSheet(ui->baseBox->styleSheet().append(QString("margin-left:50%; margin-right:50%;")));
based on How to add style via setStyleSheet() without losing orignal style in Qt?
However, I am running into the same problem.
How can I do both of these things ? Why is my check box disappearing ?

How to avoid ugly overlap with too many dockwidgets in QMainWindow?

In our application, we have a variable number of dockwidgets because some of them are added by plugins that are loaded at runtime. Not all dockwidgets need to be visible at the same time necessarily. This depends strongly on what the user is working on and what plugins are active.
However, if too many dockwidgets are added programmatically with addDockWidget(...), they start to overlap each other (not in terms of tabs, but in terms of content of one being painted on the area of a different one, which obviously looks broken).
The user can move the dockwidgets to dockareas that still have space left, but the layout/main window successfully prevents (untabbed) re-addition to the "crowded" dockarea.
We do allow tabbed docks to allow the user to arrange the dockwidgets a required, but we don't want to enable QMainWindow::ForceTabbedDocks since this would constrain the number of simultaneously visible dockwidgets too much (one per dock area).
How can I prevent this or better control how dockwidgets are added?
Not answering your question directly but it might be worthwhile to actually forget about Qt and actually think of how the whole interaction should work. What are the user expectations? What should actually happen if 10 different plugins become active? Should they be docked or should they be floating or should they become pin-able docking windows with initial state as a small button on the MainWindow edges? I think once you do that ground work and come up with user interface mock-ups, you can then start looking at Qt and figure out if Qt provides a direct way to develop that interface and if not what additional components you will need to develop to get that interface working.
From my own experience, I had developed a similar interface long back but in MFC. The way we did it was that some of the docked windows were deemed to be must have and they would come up as docked. Then there were a set of windows that didnt need to be visible always but should be quickly available and their initial state was as hidden pin-able dock window which meant they came up as buttons on the MainWindow edge. Finally there was a third set that was not required by the user always and could be called in from File->View Menu. Once the user made it visible, the user typically would assign it to one of the first two groups or keep it afloat. This whole configuration was saved in a config file and from there onwards whenever the plugin was loaded/became active the last used state of the associated docking window was used. It though involved quite a bit of extra work but the end result was to the satisfaction of all users.
Have you tryed setDockOptions(QMainWindow::AllowNestedDocks)? I can't test it now but it may help.
By default, QMainWindow::dockOptions is set to AnimatedDocks | AllowTabbedDocks so you would want something like
setDockOptions(QMainWindow::AllowNestedDocks | QMainWindow::AnimatedDocks | QMainWindow::AllowTabbedDocks)
EDIT:
If you are having too many problems, you may be going about this the wrong way. Instead of using docks, you may want to try using QMdiArea with QMdiWindow. This may not work for your program, but its something to think about.
This is the solution I tried:
I created in QTCreator an empty project with a window, a minimalistic menu labelled "New Dock" and a DockWidget named dockWidget
This is the triggered() handler for my menu item:
void MainWindow::on_actionNew_Dock_triggered()
{
QDockWidget* w = new QDockWidget("Demo", ui->dockWidget);
this->addDockWidget(Qt::LeftDockWidgetArea,w);
this->tabifyDockWidget(ui->dockWidget,w);
}
tabifyDockWidget(QDockWidget* first, QDockWidget* second) is a QMainWindow method that stacks the second dockwidget upon the first one. Hope it helps...

How to affect definition of COMBOBOX window class

I have a subclass of CComboBox that behaves differently in two different
contexts - both in native C++. In the working (test) context the top-level application was created
using VS2008. In the non-working case the application is greatly more complex
and was probably originally created by an earlier VS version.
In the failure case the control won't display a dropdown box, but responds to
up/down arrow key controls. Also in the failure case it always returns zero
when queried using GetMinVisible and always returns failure from
SetMinVisibleItems.
It appears that the problem is rooted in the definition of the COMBOBOX window
class. I have dumped the data returned from ::GetClassInfoEx in both cases.
Aside from the address of the wndProc, there are two differences. First, the working
case has style CS_GLOBALCLASS set while the failure case doesn't. Also the
working case specifies 4 bytes of extra window storage (cbWndExtra) while the
failure case specifies 8 bytes.
So my questions are: 1) What configuration properties might affect the definition of
the COMBOBOX window class?, and 2) How can I determine the correct wndProc to use if I make my own window class?
EDIT: The behavior of the dropdown box changes with the specification of the character set property in the General property tab. It works as expected if character set is unicode and fails when it's not set. The combo box subclass is in an ActiveX control, but the setting at the application level seems to affect the window class, which winds up messing up the combo box in ActiveX control. Interestingly there are other combo boxes, albeit in dialogs, elsewhere in the application that seem to behave just fine.
SetMinVisibleItems uses CB_SETMINVISIBLE and it requires comctl32 v6, this is your clue that this is a visual styles issue.
The older common controls version of the combobox actually uses the controls height as the size of the drop-down list so if you adjust that you might be able to fix it without adding a manifest.

Right-aligned tab items in Win32 tab control

I am trying to create a tab-control that have the tab-buttons aligned from right-to-left, in Win32/c++. The WS_EX_LAYOUTRTL flag doesn't help me, as it mirrors the drawing completely both for the tab items and the tab page contents. The application itself handles the mirroring automatically (it's a cross platform UI solution), which is also a reason for us not to use WS_EX_LAYOUTRTL flag (we have mirroring implemented in a generic way for all UI frameworks/platforms).
One solution would be to override TCM_GETITEMRECT and TCM_HITTEST in the subclassed TabCtrls window procedure. This enables me to move the buttons allright, but the mouse events still acts on the positions that the control "knows" the buttons really are at (ie. mouseover on the first button invalidates the leftmost button - the coordinates are not mirrored).
So that seems to be a dead end for me.
Another possibility would be to insert padding before the first tab button, to push them all to the right edge. I haven't been able to figure out how to do that, though. Visual Studio sports this little dialog:
How did they put the buttons in front of the first tab page? Knowing this would enable me to solve this problem.
Update, solution:
The solution to my problem is to use the built-in RTL support. For this to work, the tab control must have both the WS_EX_LAYOUTRTL and WS_EX_NOINHERITLAYOUT flags. That will preserve the function of all existing drawing code while only the TabCtrl buttons are mirrored. I didn't realize that the ES_EX_NOINHERITLAYOUT flag goes on the parent (the TabCtrl), which is why I was looking for the workaround originally described.
For reference, I am still curious to have an answer to the original question, though.
If you take a look with a spy application you will see that it is not actually a normal windows tab-control but custom thing and the drawing is done by the parent window AFAIK:
Both Visual Studio and Office use a lot of custom controls, some of the features make their way into the common controls after a few years, some features stay private...

Displaying multiple simultaneous Views of the same Document

How do I persuade the MFC Doc/View architecture to let me simultaneously display two different views of the same document?
For example, say my CDocument sub-class represents an archive of some description.
I want a UI where the names of all the entries in that archive are presented in a CListView sub-class in the left hand pane, while the details of the currently selected entry are displayed in a CEditView sub-class in the right hand pane.
The CSingleDocTemplate only seems to allow for connecting up one document, one frame and one view. I still want an SDI application, but I want one document and two different views - isn't that the whole point of a good Doc/View architecture?
SDI means "Single Document Interface", it restricts you to only one single document at a time but not in the number of views you can open for this document.
The probably most common approach to open multiple views in an SDI application are Splitter Windows.
You add one view to the CSingleDocTemplate (it doesn't matter which one)
pDocTemplate = new CSingleDocTemplate(
IDR_MYRESOURCEID,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMyFrameWnd),
RUNTIME_CLASS(CMyListView));
Your frame window gets an instance of a CSplitterWnd m_wndSplitter and you overload the OnCreateClient virtual function:
BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
VERIFY(m_wndSplitter.CreateStatic(this,1,2)); // one row / two columns
VERIFY(m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CMyListView),
CSize(300,300),pContext));
VERIFY(m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CMyEditView),
CSize(300,300),pContext));
return TRUE;
}
This example creates a splitter window with one row and two columns. On the left side in the splitter is a view of type CMyListView and on the right side is a view of type CMyEditView.
You can even nest multiple splitter windows one in each other to create arbitrary complex view collections in the frame window.
Here is a small tutorial which shows how to work with splitter windows in a SDI application:
http://www.codeproject.com/KB/splitter/splitterwindowtutorial.aspx
Edit
Wiring up of the views you add to the splitter with the document does MFC internally: CCreateContext* pContext which is passed into OnCreateClient contains a reference m_pCurrentDoc to the current document (the Framewindow knows about this document). MFC uses this in CView::OnCreate (ViewCore.cpp) to add the View to the Document: m_pCurrentDoc->AddView(this) and to set the document pointer m_pDocument in the View.
Therefore subsequent calls of UpdateAllViews of your document will take care of both views.
Revised based on comment:
Okay, what you're after is a static splitter window. The easiest way (I know of) to create this is to start with an SDI MFC project, and tell it that you want a splitter window (in the AppWizard, under "User Interface Features", check "Split Window"). That will create a dynamic splitter -- i.e., it starts with only one pane, and you can create a second by dragging the splitter bar -- but when you do, you'll just get two identical views (though you can scroll them separately from each other).
Then we have to do a little work to turn that from a dynamic splitter into a static splitter. It's probably best to start by looking at the code for the dynamic splitter. If you look in that app's CMainFrame, you'll find that it has:
CSplitterWnd m_wndSplitter;
If you look in the main-frame's OnCreateClient, you'll find something like this:
return m_wndSplitter.Create(this,
2, 2, // TODO: adjust the number of rows, columns
CSize(10, 10), // TODO: adjust the minimum pane size
pContext);
This is what we need to change -- the Create is what creates the dynamic splitter. We need to get rid of that, and create a static splitter instead. The first step to do that is create another view class -- right now, we have only one view class, but we want two, one for each pane.
The easiest way (that I know of) to create our second view class is to run a second copy of VS, and create another (separate) application. We'll tell it to base the view class for that application off of CListView. We'll then take the files for that view, and add them to our original project. To make it easy to hook things up, we want to make sure this second project uses the same name for its document class as the first one did though.
At that point, we have the code for our second view, but it's not connected to anything else, so the view it creates won't be visible. To make it visible, we need to include its header into the CMainframe.cpp (or whatever name it has in your target project). We then get back to the OnCreateClient, and replace the code quoted above with something like this:
CRect rect;
GetClientRect(&rect);
BOOL ret = m_wndSplitter.CreateStatic(this, 2, 1); // 2 rows, 1 column of views
// row 0, column 0 will be the "OriginalView". The initial split will be
// in half -- i.e., each pane will be half the height of the frame's client
//
m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(OriginalView), CSize(1, rect.Height()/2), pContext);
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(ListBasedView), CSize(1, rect.Height()/2), pContext);
For the moment, I've created a horizontal split, with the "OriginalView" in the upper pane, and the "ListBaseView" in the lower pane, but (I think) it should be fairly obvious what changes to make to rearrange the views.
From there, of course, you'll have to write the code in each view to do whatever it is that view is supposed to do -- but since each is still a separate, normal view, each is reasonably independent, so the development is about like normal. The only significant difference is that you must follow the rules for invalidating your document, and (especially if one of the views is expensive to update) you may want to consider using hints to tell what part of the data has been invalidated so you can write each view to update only what's needed instead of just re-drawing all its data every time.