Create keyboard settings file; keyboard settings for recent files

This commit is contained in:
Rebecca Breu 2021-10-05 10:44:03 +02:00
parent 6c1da756aa
commit 5475f5d843
9 changed files with 140 additions and 11 deletions

View file

@ -5,6 +5,8 @@ Added
-----
* Show list of recent files on welcome screen
* Keyboard shortcuts can now be configured via a settings file.
Go to "Settings -> Open Settings Folder" and edit KeyboardSettings.ini
Fixed
-----

View file

@ -265,4 +265,9 @@ actions = [
'checkable': True,
'callback': 'on_action_always_on_top',
},
{
'id': 'open_settings_dir',
'text': 'Open Settings Folder',
'callback': 'on_action_open_settings_dir',
},
]

View file

@ -98,6 +98,12 @@ menu_structure = [
'arrange_vertical',
],
},
{
'menu': '&Settings',
'items': [
'open_settings_dir',
],
},
{
'menu': '&Help',
'items': [

View file

@ -112,16 +112,26 @@ class ActionsMixin:
files = self.settings.get_recent_files(existing_only=True)
items = []
i = -1
for i, filename in enumerate(files):
qaction = QtGui.QAction(os.path.basename(filename), self)
action_id = f'recent_files_{i}'
key = 0 if i == 9 else i + 1
if key < 10:
qaction.setShortcuts([f'Ctrl+{key}'])
shortcuts = KeyboardSettings().get_shortcuts(
'Actions', action_id, [f'Ctrl+{key}'])
qaction.setShortcuts(shortcuts)
qaction.triggered.connect(partial(self.open_from_file, filename))
self.addAction(qaction)
self._recent_files_submenu.addAction(qaction)
self.bee_actions[f'recent_files_{i}'] = qaction
items.append(f'recent_files_{i}')
self.bee_actions[action_id] = qaction
items.append(action_id)
# Set shorcuts in settings file for remaining slots:
for j in range(i + 1, 10):
key = 0 if j == 9 else j + 1
KeyboardSettings().get_shortcuts(
'Actions', f'recent_files_{j}', [f'Ctrl+{key}'])
def _clear_recent_files(self):
for action in self._recent_files_submenu.actions():

View file

@ -143,6 +143,8 @@ class BeeSettings(QtCore.QSettings):
class KeyboardSettings(QtCore.QSettings):
save_unknown_shortcuts = True
def __init__(self):
settings_format = QtCore.QSettings.Format.IniFormat
filename = os.path.join(
@ -159,7 +161,12 @@ class KeyboardSettings(QtCore.QSettings):
values = list(filter(lambda x: x, values.split(', ')))
logger.debug(f'Found custom shortcuts for {group}/{key}: {values}')
return values
return default or []
values = default or []
if self.save_unknown_shortcuts:
self.set_shortcuts(group, key, values)
return values
def logfile_name():

View file

@ -489,6 +489,11 @@ class BeeGraphicsView(MainControlsMixin,
return
logger.info('No image data or text in clipboard')
def on_action_open_settings_dir(self):
dirname = os.path.dirname(self.settings.fileName())
QtGui.QDesktopServices.openUrl(
QtCore.QUrl.fromLocalFile(dirname))
def on_selection_changed(self):
logger.debug('Currently selected items: %s',
len(self.scene.selectedItems(user_only=True)))

View file

@ -1,5 +1,5 @@
import os.path
from unittest.mock import patch, MagicMock
from unittest.mock import patch, MagicMock, call
from PyQt6 import QtWidgets
@ -26,8 +26,10 @@ class FooWidget(QtWidgets.QWidget, ActionsMixin):
@patch('PyQt6.QtGui.QAction.toggled')
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
@patch('beeref.actions.mixin.KeyboardSettings.get_shortcuts')
def test_create_actions(
actions_mock, menu_mock, toggle_mock, trigger_mock, qapp):
kb_mock, actions_mock, menu_mock, toggle_mock, trigger_mock, qapp):
kb_mock.side_effect = lambda group, key, default: default
widget = FooWidget()
actions_mock.__iter__.return_value = [{
'id': 'foo',
@ -47,6 +49,7 @@ def test_create_actions(
assert qaction.shortcut() == 'Ctrl+F'
assert qaction.isEnabled() is True
assert widget.bee_actions['foo'] == qaction
kb_mock.assert_called_once_with('Actions', 'foo', ['Ctrl+F'])
@patch('beeref.actions.mixin.menu_structure')
@ -267,7 +270,10 @@ def test_build_menu_and_actions_disables_actiongroups(
@patch('PyQt6.QtGui.QAction.triggered')
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
def test_create_recent_files(actions_mock, menu_mock, triggered_mock, qapp):
@patch('beeref.actions.mixin.KeyboardSettings.get_shortcuts')
def test_create_recent_files_more_files_than_shortcuts(
kb_mock, actions_mock, menu_mock, triggered_mock, qapp):
kb_mock.side_effect = lambda group, key, default: default
widget = FooWidget()
widget.settings.get_recent_files.return_value = [
os.path.abspath(f'{i}.bee') for i in range(15)]
@ -295,6 +301,69 @@ def test_create_recent_files(actions_mock, menu_mock, triggered_mock, qapp):
assert qaction15.isEnabled() is True
assert widget.bee_actions['recent_files_14'] == qaction15
assert kb_mock.call_count == 10
kb_mock.assert_has_calls(
[call('Actions', 'recent_files_0', ['Ctrl+1']),
call('Actions', 'recent_files_9', ['Ctrl+0'])],
any_order=True)
@patch('PyQt6.QtGui.QAction.triggered')
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
@patch('beeref.actions.mixin.KeyboardSettings.get_shortcuts')
def test_create_recent_files_fewer_files_than_shortcuts(
kb_mock, actions_mock, menu_mock, triggered_mock, qapp):
kb_mock.side_effect = lambda group, key, default: default
widget = FooWidget()
widget.settings.get_recent_files.return_value = [
os.path.abspath(f'{i}.bee') for i in range(5)]
menu_mock.__iter__.return_value = [{
'menu': 'Open &Recent',
'items': '_build_recent_files',
}]
widget.build_menu_and_actions()
triggered_mock.connect.assert_called()
assert len(widget.actions()) == 5
qaction1 = widget.actions()[0]
assert qaction1.text() == '0.bee'
assert qaction1.shortcut() == 'Ctrl+1'
assert qaction1.isEnabled() is True
assert widget.bee_actions['recent_files_0'] == qaction1
qaction5 = widget.actions()[4]
assert qaction5.text() == '4.bee'
assert qaction5.shortcut() == 'Ctrl+5'
assert qaction5.isEnabled() is True
assert widget.bee_actions['recent_files_4'] == qaction5
assert kb_mock.call_count == 10
kb_mock.assert_has_calls(
[call('Actions', 'recent_files_0', ['Ctrl+1']),
call('Actions', 'recent_files_9', ['Ctrl+0'])],
any_order=True)
@patch('beeref.actions.mixin.menu_structure')
@patch('beeref.actions.mixin.actions')
@patch('beeref.actions.mixin.KeyboardSettings.get_shortcuts')
def test_create_recent_files_when_no_files(
kb_mock, actions_mock, menu_mock, qapp):
kb_mock.side_effect = lambda group, key, default: default
widget = FooWidget()
widget.settings.get_recent_files.return_value = []
menu_mock.__iter__.return_value = [{
'menu': 'Open &Recent',
'items': '_build_recent_files',
}]
widget.build_menu_and_actions()
assert len(widget.actions()) == 0
assert kb_mock.call_count == 10
kb_mock.assert_has_calls(
[call('Actions', 'recent_files_0', ['Ctrl+1']),
call('Actions', 'recent_files_9', ['Ctrl+0'])],
any_order=True)
@patch('PyQt6.QtGui.QAction.triggered')
@patch('beeref.actions.mixin.menu_structure')

View file

@ -17,6 +17,10 @@ def pytest_configure(config):
import logging.config
logging.config.dictConfig = MagicMock
# Disable creation of KeyboardSettings.ini to speed tests up
from beeref.config import KeyboardSettings
KeyboardSettings.save_unknown_shortcuts = False
@pytest.fixture(autouse=True)
def commandline_args():

View file

@ -83,15 +83,36 @@ def test_keyboardsettings_set_shortcuts_multiple(kbsettings):
assert kbsettings.get_shortcuts('Actions', 'foo') == ['Ctrl+F', 'Alt+O']
def test_keyboardsettings_get_shortcuts_existing(kbsettings):
kbsettings.set_shortcuts('Actions', 'bar', ['Ctrl+R'])
with patch.object(kbsettings, 'set_shortcuts') as set_mock:
with patch.object(kbsettings, 'save_unknown_shortcuts', True):
shortcuts = kbsettings.get_shortcuts('Actions', 'bar', ['Ctrl+B'])
assert shortcuts == ['Ctrl+R']
set_mock.assert_not_called()
def test_keyboardsettings_get_shortcuts_default(kbsettings):
assert kbsettings.get_shortcuts('Actions', 'bar', ['Ctrl+B']) == ['Ctrl+B']
with patch.object(kbsettings, 'set_shortcuts') as set_mock:
with patch.object(kbsettings, 'save_unknown_shortcuts', True):
shortcuts = kbsettings.get_shortcuts('Actions', 'bar', ['Ctrl+B'])
assert shortcuts == ['Ctrl+B']
set_mock.assert_called_once_with('Actions', 'bar', ['Ctrl+B'])
def test_keyboardsettings_get_shortcuts_defaults_dont_overwrite_empty(
def test_keyboardsettings_get_shortcuts_default_doesnt_override_empty(
kbsettings):
kbsettings.set_shortcuts('Actions', 'bar', [])
assert kbsettings.get_shortcuts('Actions', 'bar', ['Ctrl+B']) == []
with patch.object(kbsettings, 'set_shortcuts') as set_mock:
with patch.object(kbsettings, 'save_unknown_shortcuts', True):
shortcuts = kbsettings.get_shortcuts('Actions', 'bar', ['Ctrl+B'])
assert shortcuts == []
set_mock.assert_not_called()
def test_keyboardsettings_get_shortcuts_not_set_no_defaults(kbsettings):
assert kbsettings.get_shortcuts('Actions', 'baz') == []
with patch('beeref.config.KeyboardSettings.set_shortcuts') as set_mock:
with patch.object(kbsettings, 'save_unknown_shortcuts', True):
shortcuts = kbsettings.get_shortcuts('Actions', 'baz')
assert shortcuts == []
set_mock.assert_called_once_with('Actions', 'baz', [])