Which class should take care of shortcuts? - c++

I'm coding a simple text editor with only few basic features.
At the moment it has a QMainWindow with QTabWidget set as it's centralWidget.
What I'm implementing now are some shortcuts; ctrl+s to save, ctrl+o to open, and ctrl+t/ctrl+w to create a new tab/close the current tab.
I know how to implement all the features, but the question is where to implement the features?
Should all the shortcuts be members of QMainWindow and let it take care of everything, or should I try separating the shortcuts into their correspoding classes?
For example, creating a new tab (ctrl+t) would be part of QTabWidget.
Now what about ctrl+s (save, duh), should it be part of QTextEdit since it's the text I'm saving, or..?
Basically my program contains three classes; QMainWindow, which contains QTabWidget, and each tab is a QTextEdit.

Your setup sounds a lot like many of the applications I've built.
I generally handle shortcuts via the QAction::setShortcut() method, so it's really more about where does it make sense to store the QAction objects. This usually ends up being my MainWindow class as a lot of the actions are all used in the MainWindw's menus. When these actions are triggered, the result is usually that the TabWidget is notified which in turn notifies all the necessary tabs and can handle things like a "Close All" action triggering only one save prompt.
However, if it doesn't make sense to store those QActions on the MainWindow object, then I don't, as in the case of the context menu I usually have available on my individual tabs.
Hope that helps.

Related

Create a property sheet using a dialog template

I need to extend an existing MFC app with a UI that will end up very cluttered unless I use a tab control. However, the nature of the UI is that there are some controls that are global, and only some that can be localised to a particular tab.
The standard use of tab controls (CPropertySheet + CPropertyPage) more or less expects there only to be CPropertyPage instances (tabs) visible on the CPropertySheet object, and nothing else. There is a Microsoft Example Project that shows a single additional window painted outside the area occupied by the tab control... but it's not immediately clear how it is created/drawn/handled, and it is only one single additional window that generates few events (I guess it is painted, so there must be a WM_PAINT event handler lurking somewhere).
Is it possible to lay out a bunch of controls with the MS Dialog Editor, including a tab control, and create the CPropertySheet using that template, hook up event handlers in a nice way, etc... or some equivalent way of getting the MFC framework to do as much of the creating, drawing and event handling as possible when it comes to a situation like this?
Yes it is possible to create dialog templates and use them in a CPropertyPage.
Each CPropertyPage behaves nearly like a dialog and handles all events for the controls on it.
Also there are features like OnApply that help you to manage the data exchange between the controls and your internal storage.
The central CPropertySheet only creates the dialog that get active. So OnInintDialog for a page is called the first time when the page gets active.
In the MFC since 2010 are more possibilities than a CPropertySheet. You can create tabbed views, that again may be CFormViews. I don't like CDialog based applications so I would prefer a tabbed view in a standard frame with toolbar and menus if appropriate for the application. So another method to unclutter your UI is to choose the MDI interface with tabbed documents... but multiple documents maybe isn't what you want.
Here is a sample of an SDI application with multiple tabbed views.
Also Coeproject shows some more samples here and with splitters and tabs here.
There are at least three solutions paths:
Try to squeeze the situation into the CPropertySheet + CPropertyPage framework which does not naturally allow for additional dialog controls on the CPropertySheet object, and so you will get no framework support this
Place a tab control on an ordinary dialog, and then use the TCN_SELCHANGE messages to fire code that manually hides & shows individual dialog controls subject to the tab control (again no framework support, but this time "within" the tab control instead of outside it)
Follow Mark Ransom's observation that if you can embed one kind of CWnd-based control on a CPropertySheet then you can probably embed any such object, including a CDialog-based object that has been developed in the MFC Dialog Editor
All of these approaches are going to encounter challenges, and it will depend on the specifics of the situation as to which is better. But first you should really consider whether there is a cleaner UI design which would lend itself to a simpler solution.
In my specific case, I saw no cleaner design alternatives, and found it easiest to take the second approach. It left me with some reasonably simple calls to ShowWindow() to show/hide the controls inside the tab control.

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 use the same QAction in two QMenus with different text?

So I have an action I want to expose in multiple menus (one of the main menus, and some context menus). I'd like the menu item text to differ slightly from menu to menu (to make the mnemonic unique, and to be more or less verbose as necessary in each context).
In MFC (which I have the pleasure of migrating away from at the moment) this was easy, as each menu's items were defined separately, and could map to the same ID, which would be linked to the handler. In Qt though, the QAction encapsulates the behaviour as well as the text/icon/etc. So I don't suppose there's straightforward support for it to return a different text dependent on where it's being used.
My thought about how to handle this is, for each location, to create a "proxy" QAction, which has the text specific to that context and simply has its triggered() signal connected to the original QAction's one. But I thought I should check first about whether there's an easier way to approach this.
I don't know about MFC, but in Qt - QAction is just an interface. One QAction object can have only one text to display. But the real action QAction does, you will implement in what Qt calls SLOT. Then you can have as much interfaces(or QActions objects) pointing to the same slot - just connect all QAction objects, that you want to do the same thing, to the same slot.
Hope this helps.
Your suggested solution is the simplest, I think. You can change the text of an action dynamically, when a menu is activated, but this looks more complicated to me.

How to make Qt delegate editors 'sticky'

I'm migrating an application from a homegrown UI to Qt. One of the most important controls is the property panel, which takes an object that implements my reflection API's interface and spits out a dialog box containing editors for all the properties.
I've written an implementation of QAbstractItemModel for my property system and I've written a few handlers for various types inside of a QTableView. I've also written a QItemDelegate to create editors for the properties.
The trouble is that I'd like the editors to hang around, rather than be strictly popups. This is so that they can handle the rendering of the property, require less clicks to operate and also not disappear as soon as something else gets the focus, such as my colour button - the editor (which has the slots listening for colour changes) disappears as soon as the colour picker dialog appears, which means that nothing is then listening for changes.
I can't find any options for making the editors persist. Am I barking up the wrong tree here or is there a more appropriate way of doing this? I've tried to do things the 'Qt' way but I'm already hitting brick walls.
Thanks,
There is the QAbstractItemView::openPersistentEditor() method.

Qt-GUI with several "pages", how to synchronize the size of several QWidgets

I am currently writing a wizard-style application using Qt4. I am not using the wizard class however, since going next/back does not make sense from every step in the process.
The user starts off at a "hub"-window with five buttons, each of which take him to another window at a different stage of the process. At the beginning all but the first button of the hub are disabled. After clearing each stage, the user will be directed back to the hub and one new button/stage will get enabled.
Since every stage has significantly differing objectives, I coded every single stage as a single QWidget that gets instantiated in the main() function. To switch between the widgets, I simply hide() the one I am leaving and show() the one I am going to.
However, this leads to problems if the user resized or moved one widget, since the next widget will always show up at its default position instead of the new one.
Is there a way to synchronize the widgets' sizes and positions?
Or, better yet, is there a more elegant way to deal with those different windows than switching between several hidden widgets?
Thank you in advance!
Create one top-level widget that holds the others.
I suggest that you either use QStackedWidget, or, if you want more control, create your own widget and use QStackedLayout directly.
Why not just have one main QWidget as a container for your pages? That way if the user moves the main QWidget and then goes to the next page, it will still open in the new position inside the main widget.
Generally, I've never had occasion to create multiple widgets inside my main method. Don't quite see the point.
I'm beginning on something similar - with different views (perspectives) for different tasks along the way. Using toolbar icons and file menu, not buttons, to move between views. Similar to how MS Outlook allows you to have the window display email, or a calendar, or contacts, etc.
My intent (haven't started yet) is to have one QMainWindow as the application window, containing my various QWidgets that offer the various views. Depending on what task the user is doing, one composite widget will be visible, and the others hidden.