Repository navigation
wxGUI: Delete shortcut for the Layers pane and the Data Catalog - #8051
polucifier wants to merge 2 commits into
Conversation
Each layer item carries an options button which is a child of the tree, and it can take the keyboard focus when clicked. Key events do not propagate from a child to its parent, so the Delete shortcut bound on the tree was never invoked while such a button was focused, and the key was simply discarded. Bind the same shortcut on the button. Removing a layer destroys the button which is dispatching the event, so the removal is deferred with CallAfter, the same way the button click handler already avoids destroying itself on some platforms.
Bind the Delete key to the removal actions offered by the context menus of the data catalog, and Ctrl+Shift+Delete to deleting a GRASS database from disk. The conditions are the same as for the context menu items, so that the shortcuts never remove more than the menus offer. Removal is handled on key up rather than key down, so that holding the key does not remove several items in a row. This matches the layer tree and the graphical modeler. Addresses the delete shortcuts proposed in OSGeo#5425 for the Data Catalog.
ae3c04a to
09c77d4
Compare
|
@saket0187 would you be able to review this? |
saket0187
left a comment
There was a problem hiding this comment.
It looks good in general. Here are a few suggestions:
- Delete on a database node removes it from the catalog tree without any confirmation; if possible, please add one as for maps and mapsets.
- Not for this PR: The Yes/No buttons are in different positions in the delete map and in other dialogs, and I deleted a mapset by mistake because of it :( , this is worth a follow-up PR.
- About space-time datasets: maybe Delete = "Delete dataset only" (maps are kept, the safer choice) and Ctrl+Shift+Delete = "Delete dataset and maps", like the database case? Both already ask for confirmation.
|
|
||
| Nodes below the database in the tree populate the selected database as | ||
| well, so the node type has to be checked explicitly. Only a database | ||
| node itself can be removed. | ||
| """ | ||
| if ( | ||
| self.mixed | ||
| or self.selected_layer[0] | ||
| or self.selected_stds[0] | ||
| or self.selected_mapset[0] | ||
| or self.selected_location[0] | ||
| or len(self.selected_grassdb) != 1 | ||
| ): | ||
| return None | ||
|
|
||
| return self.selected_grassdb[0] |
There was a problem hiding this comment.
Optional: since this only needs "exactly one item, and it's a database", it could check that directly.
| Nodes below the database in the tree populate the selected database as | |
| well, so the node type has to be checked explicitly. Only a database | |
| node itself can be removed. | |
| """ | |
| if ( | |
| self.mixed | |
| or self.selected_layer[0] | |
| or self.selected_stds[0] | |
| or self.selected_mapset[0] | |
| or self.selected_location[0] | |
| or len(self.selected_grassdb) != 1 | |
| ): | |
| return None | |
| return self.selected_grassdb[0] | |
| """ | |
| selected = self.GetSelected() | |
| if len(selected) == 1 and selected[0].data["type"] == "grassdb": | |
| return selected[0] | |
| return None |
|
|
||
| if self.selected_layer[0]: | ||
| _, _, currentMapset = self._isCurrent(gisenv()) | ||
| return self.OnDeleteMap if currentMapset else None |
There was a problem hiding this comment.
Not related to this PR, but this check has no effect: SetRestriction() is never called, so _restricted is always False and _isCurrent() always returns True. @petrasovaa should the restriction code be removed in a follow-up, or is it meant to come back?
There was a problem hiding this comment.
Remove in follow up, and it doesn't need to be added here.
| if event.ControlDown() and event.ShiftDown(): | ||
| # Deleting a database from disk cannot be undone by adding it | ||
| # back to the catalog, so it needs an extra modifier | ||
| if self._selectedGrassDb() and not self._restricted: | ||
| self.OnDeleteGrassDb(None) | ||
| return | ||
| else: | ||
| handler = self._getRemoveHandler() | ||
| if handler: | ||
| handler(None) | ||
| return |
There was a problem hiding this comment.
| if event.ControlDown() and event.ShiftDown(): | |
| # Deleting a database from disk cannot be undone by adding it | |
| # back to the catalog, so it needs an extra modifier | |
| if self._selectedGrassDb() and not self._restricted: | |
| self.OnDeleteGrassDb(None) | |
| return | |
| else: | |
| handler = self._getRemoveHandler() | |
| if handler: | |
| handler(None) | |
| return | |
| modifiers = event.GetModifiers() | |
| if modifiers == wx.MOD_CONTROL | wx.MOD_SHIFT: | |
| # Deleting a database from disk cannot be undone by adding it | |
| # back to the catalog, so it needs an extra modifier | |
| if self._selectedGrassDb() and not self._restricted: | |
| self.OnDeleteGrassDb(None) | |
| return | |
| elif modifiers == wx.MOD_NONE: | |
| handler = self._getRemoveHandler() | |
| if handler: | |
| handler(None) | |
| return |
Any Delete with other modifiers works (e.g. Shift+Delete) here. Maybe check the modifiers exactly with event.GetModifiers()?
| Close current map display | ||
|
|
||
| Delete: | ||
| Delete selected layer |
There was a problem hiding this comment.
| Delete selected layer | |
| Remove selected map layer(s) |
| the catalog, so it can be added back later. Nothing is removed when | ||
| several databases are selected or when items of different types are | ||
| selected together.</dd> | ||
| <dt>Ctrl+Shift+Delete</dt> |
There was a problem hiding this comment.
I would suggest removing this, it's rather rare and not expected.
I am not sure whether Ctrl+Shift+Delete is all that useful, user would have to know about it, while simple delete is obvious. It seems the action would be rare enough to just select it from context menu. But feel free to disagree, I know many people use shortcuts more than I. But I agree with the Delete = "Delete dataset only" (maps are kept, the safer choice) |
| the key does not remove several items in a row. This matches the | ||
| layer tree and the graphical modeler. | ||
| """ | ||
| if event.GetKeyCode() != wx.WXK_DELETE: |
There was a problem hiding this comment.
| if event.GetKeyCode() != wx.WXK_DELETE: | |
| if event.GetKeyCode() not in {wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE}: |
Consider handling wx.WXK_NUMPAD_DELETE as well, so the Delete key on a numeric keypad is supported.
Adds the Delete shortcut for removing items in the Data Catalog, and fixes the
Layers pane one, which has been in the code for the entire history of the
project but never worked.
This covers the delete shortcuts from #5425 that were suggested in #7423. The
Data Catalog and the Layers pane are in the same PR because both were asked for
and the changes are small and independent. They are kept in separate commits so
they are trivial to split if you prefer.
Data Catalog
DeleteDeleteDeleteDeleteCtrl+Shift+DeleteThe conditions mirror the context menu items, including the ones that disable
them, so the shortcuts never remove more than the menus offer:
unregister it
Layers pane
LayerTree.OnKeyUpbindsEVT_KEY_UPand dispatchesWXK_DELETEto the layermanager, and both are already present in the parent of
c730ae7e6,
the 2011-11-24 reorganisation that moved
layertree.pyto its current path,gui/wxpython/lmgr/layertree.py. So nothing was ever missing from the handler,it simply never ran.
Each layer item carries an options button, which is a child of the tree, and the
Delete key event is delivered to that button rather than to the tree, so a
binding on the tree never sees it. This does not depend on clicking the button:
selecting a layer row with the mouse and pressing Delete behaves exactly the
same, and in both cases the key never reaches
LayerTree.OnKeyUp, so theshortcut has never been usable from the Layers pane.
The shortcut is now bound on the button as well, through a
_createLayerButton()helper shared byAddLayerandRecreateItemso the twocall sites cannot drift apart. Removing a layer destroys the button which is
dispatching the event, so the removal is deferred with
CallAfter, the same waythe button click handler already avoids destroying itself on some platforms.
event.Skip()is called on every path, so that the button keeps handling spaceand enter itself.
Testing
Both panes were tested manually in the wxGUI, going through every selection
type listed above, including the cases that must be refused, several items
selected at once, a node being renamed, and Delete with nothing selected.
Not included
Space-time datasets are left out on purpose. The context menu offers both
delete dataset only and delete dataset and maps as separate items, and a
single key cannot choose between them. It would be good to get an opinion on how
this should behave before implementing it.
Related findings, deliberately left out of this PR
I didn't find an existing issue for any of these; they all came out of testing this change.
CustomTreeCtrl.__init__bindsEVT_KEY_DOWNtoself.OnKeyDown, whichGRASS's
LayerTreeoverrides with a bareevent.Skip(), so the widget's ownkey handling never runs.
to the layer text instead of staying at the right edge of the row. This
reproduces on the current release with two or more layers, both through the
button's context menu and through the Delete shortcut.
_isCurrent()returns(True, True, True)unconditionally unless_restrictedis set, andSetRestrictionis never called, soitem.Enable(currentMapset)is currently a no-op in the popup builders. Theshortcut reproduces the same condition so that it matches the menu, but the
underlying issue looks worth its own report.
Addresses the delete shortcuts proposed in #5425 for the Data Catalog and the
Layers pane.