In applications, a context menu should generally be available, in which on the one hand certain standard commands (edit, delete, ...) always appear, but on the other hand also existing application-specific functionalities are offered, which are available for the currently selected object - either globally in the application, or specifically in individual views.
The application developer should be able to decide which of the entries are enabled/disabled for a specific object (by code, for example, depending on an attribute value of the object). The enabled/disabled status must be the same as for the corresponding command in the toolbar, button line or burger menu.
User interface
The context menu should be accessible via right mouse button on the one hand, on the other hand also via other means for touch devices.
Functionalities
Standard functionalities should always appear in the context menu, but must be able to be disabled in the application in a view-specific way (e.g. delete, edit).
Further functionalities should be able to be added type-specifically by the applications, and should then be available in all views of the application for objects of this type (e.g. "Rename", "Send (Mail)").
Further functionalities shall be able to be added by applications type- and view-specific, and shall then be available in the respective view of the application for objects of this type.
Configurability
It should be possible for the application developer to configure existing commands in burger menu, button line, toolbar into the context menu.
The application developer must generally be able to decide which of the entries should be enabled/disabled for a specific object (by code, for example depending on an attribute value of the object). The enabled/disabled state must always be the same as for the corresponding command in the icon bar or burger menu.
Implementation
See TL/ContextMenu
Code migration
- Commands that have an icon, were previously displayed in the toolbar, and now also appear in the context menu must be checked to see if they work on both backgrounds. It may be necessary to use an icon font icon instead that can adjust its Fordergund color. This was done for the edit/save/cancel icons.
- All methods in BoundChecker must get the object to be checked as argument and must not implicitly include the current model (e.g. of the component)
- BoundChecker.hideReason(Object)
- getCurrentObject(BoundCommandGroup, Object)
- Methods at BoundChecker which are not passed a current object can only be called at BoundCheckerComponent and must not be included in the authorization check of commands (only in display check e.g. after tab change)
- allow(BoundCommandGroup)
- allow(BoundCommand)
- Commands which do not require the current component model (e.g. "Refresh") must be given null() as target configuration, so that they are not included in the context menu of elements of parent components. This configuration can be done either by setting target="null()" when configuring the command, or as a default annotation when implementing the command:
#!java public class InvalidateCommand extends AJAXCommandHandler { public interface Config extends AJAXCommandHandler.Config { @Override @FormattedDefault(TARGET_NULL) ModelSpec getTarget(); } ... }
- Dialog openers which use the option targetComponent="..." must additionally get target="model(self())" as configuration. Since most dialogs don't get their model explicitly set on opening, but get the model "behind the scenes" from their dialogParent(), the default setting for target of OpenModalDialogCommandHandler has changed from model(self ()) to null( ). This causes such dialog openers that do not work with a model other than the one currently set in the parent component not to be included in the context menu of parent components, e.g:
#!patch Index: webapp/WEB-INF/layouts/history/historyDialog.xml =================================================================== --- webapp/WEB-INF/layouts/history/historyDialog.xml (revision 278883) +++ webapp/WEB-INF/layouts/history/historyDialog.xml (working copy) @@ -43,6 +43,7 @@ clique="history" group="${openerCommandGroup}" resourceKey="${defaultI18n}" + target="model(self())" targetComponent="${namePrefix}Table" >${executability}</open-handler> </dialogInfo>
- If a dialog is no longer displayed, or if the opener has the wrong executability, this may be due to the changed default for target of the OpenModalDialogCommandHandler. This is the case, if e.g. an executability rule was configured at the open handler, which refers to the target model of the trader, although the opened dialog does not work with the target model of the handler at all, but gets its model "behind the scenes" from its dialog parent. In this case the dialog must be changed so that its model is set via targetComponent="..." at the open handler and the configuration target is set as described in the last point, e.g:
#!patch Index: webapp/WEB-INF/layouts/com.top_logic.contact/admin/orgUnits/EditOrgUnit_shared.xml =================================================================== --- webapp/WEB-INF/layouts/com.top_logic.contact/admin/orgUnits/EditOrgUnit_shared.xml (revision 278881) +++ webapp/WEB-INF/layouts/com.top_logic.contact/admin/orgUnits/EditOrgUnit_shared.xml (working copy) @@ -56,17 +56,23 @@ <include name="/element/createStructuredElement.xml" detailComponent="${createComponent}" jSPNewPage="${createJSP}" + model="null()" namePrefix="${namePrefix}OrgUnit" > <inject> <dialogInfo - defaultI18n="layouts.contact.EditOrgUnit_Shared.newOrgUnit" - executability="CreateElementRule" height="250" - openerClique="create" - openerCommandGroup="Create" width="450" - /> + > + <open-handler id="displayDialog_${namePrefix}OrgUnitnewElementDialog" + clique="create" + executability="CreateElementRule" + group="Create" + resourceKey="layouts.contact.EditOrgUnit_Shared.newOrgUnit" + target="model(self())" + targetComponent="${namePrefix}OrgUnitnewElementDialog" + /> + </dialogInfo> </inject> </include> </dialogs>
Open items
-
Context menu for tree components and tree controls -
Context menu for table components and table controls -
Context menu for tree table components -
Context menu for all component backgrounds -
Context menu for inline displayed objects ("resource renderer") -
Context menu for charts- Not in this ticket.
-
Type-specific context menu entries -
Resource provider provides context menu?- No: There is an additional interface ContextMenuCommandsProvider that can be used to provide context menu commands for an object. A configured variant of this is the LabelProviderService.
-
Collapse form groups via context menu- No: Too much context menu space reserved for too little functionality.
-
Context menu for form fields -
Heading for context menus -
The context menu opens directly at the mouse position, regardless of whether the entire menu can be displayed at this position. Instead, the menu must be displayed in such a way that it also fits on the screen - like the filter popups, for example.- See #24641.
Review
-
If a context menu is already open and you right click on another element, the first context menu should be closed and a new one opened. So far the context menu stays open and additionally the browser context menu opens. This is confusing for me. Because I do this most of the time, when I clicked wrong and want to open the context menu on another element.- Now, if you do the context menu click with the context menu open on the background, the original context menu is closed instead of opening the browser context menu. Unfortunately, in this situation you can't open the context menu of the new target directly, because the click doesn't hit this target but a transparent pane that lies over the complete window.
-
If you click on an object with a long name, the context menu will be as wide as the name. But in some applications the customer has very long names for his subject objects. Therefore the width of the context menu should be limited.- The title never becomes wider than the content of the menu.
-
The width of the context menu behaves strangely: When clicking on the root object in the demo type tree, the text "Create node" breaks. Probably because the context menu is too narrow. But when I display the I18N keys, it displays much wider, without wrapping text.- Menu items no longer wrap.
-
If you open the gui inspector with the context menu and click something outside the context menu, the cursor becomes a wait cursor. Only after a while you notice that nothing happens and that the wait cursor is always displayed outside the context menu. This is irritating, but on the other hand it only affects us. Is there anything that can be improved? For example, that the inspection also works there. Or that the cursor changes to "not possible" when inspecting outside the context menu.- This is not different from previous burger menus. The cursor can't be context sensitive, because during inspection there is a pane over the whole content.
-
In the grid, I can switch to edit mode in the context menu of each row. But I can't do Cancel, Save, etc. there. This is only available when I click in the table header. This seems inconsistent. Can you still add that in?- In the grid you can edit all rows via the context menu, but of course you can only save/accept/cancel the one currently edited - that's the difference. It is now built in exactly so that you see save/accept/cancel only in the context menu of the currently edited object/row. This is true not only for grids but also for trees with detail views (at least when "Edit" appears in the tree's context menu, e.g. in Structures:Aspect Inheritance).
-
In the demo table views "Frozen", "Configured Sidebar" and "Tree-based Table" I cannot open the component context menu. I can only open the one for individual rows. Is this on purpose?- The context menu for these views has no entries. The table configuration commands do not show up in the context menu. Could certainly be wished for, but is technically difficult.
-
In the toolbars I can't open the context menu for their components.- Yes, technically difficult.
-
If you parameterize the context menu providers with the type of the component, the concrete derivations don't have to cast the component every time.- If you did that( parameterizecom.top_logic.layout.basic.contextmenu.component.ContextMenuFactory ), then you wouldn't be able to call the method (without an unchecked cast).
-
In ComponentContextMenuFactory.Provider.createButtons(...), more objects are added to a list. This list comes from super.createButtons(...). The problem: super does not give any guarantees if the list is mutable or even resizable. The list is created there using createProviderButtons(...). This in turn calls toButtons(...). And that uses Collectors.toList(...). And that says: "There are no guarantees on the type, **mutability**, serializability, or thread-safety of the List returned; if more control over the returned List is required, use toCollection(Supplier)." In the Java version I use, ArrayList::new is used there, which makes it work. But with lists coming from somewhere else, you should generally be careful.- Ack.
-
TypeBasedContextMenuFactory.Provider._component you can make final.- Ok.
-
TypeBasedContextMenuFactory.Config: Properties is missing the constants for their @name.- Done.
-
ContextMenuCommandsProvider: This interface has two methods: hasContextMenuCommands and getContextCommands. For the first one a trivial default implementation can be specified: !getContextCommands(...).isEmpty(). Then it would be a functional interface and could be implemented by lambda expression. Up to now you always need a new class for this. Even if it is anonymous, it is much more lines than a lambda expression. And where optimization is possible, this method can still be implemented.- The interface has two methods exactly because one is called extremely often and therefore must be fast and the other can be expensive. Therefore the trivial default implementation is counterproductive. This is also stated (perhaps too kindly) in the documentation: "Can be used for optimizing the decision, whether a context menu should be offered."
-
ConfiguredContextMenuCommandsProvider: You can make the instance variables final. For the property override the @Name annotation is missing. For entries a constant for the value of the @Name annotation is missing.- Done.
-
GridContextMenuFactory.Provider.acceptComponentCommand(...) The method is overridden, but only calls super.- Removed.
-
GridTableConfig: The JavaDoc says: "TabConfig with defaults for GridComponent." You must have meant TableConfig instead of TabConfig. -
There are now two classes TestLabelProviderService. Both were introduced this year. One at #24380 in com.top_logic.element. And one under #24501 in com.top_logic.- This is intentional, because you can only test model references in tl-element.
-
You can make the following instance variables final:-
ContextCommandsControl._contextMenu and _titleProvider. -
ContextMenuControl._contextMenu -
AbstractMenuContents._contents - Done.
-
Test
Trees
Tables
Grids
Component commands
Inline objects
Form fields
- For form fields, Technical Demo:Layout Framework#1:Forms:Form Controls (inline) the textInputWithContextMenu field has a context menu that inserts predefined values into the field.