Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions Lib/idlelib/configdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -1146,8 +1146,8 @@ def create_page_keys(self):
selected keyset. The keybindings are loaded in load_keys_list()
and are pairs of (event, [keys]) where keys can be a list
of one or more key combinations to bind to the same event.
Mouse button 1 click invokes on_bindingslist_select(), which
allows button_new_keys to be clicked.
Mouse button 1 click or Up or Down key invokes
on_bindingslist_select(), which allows button_new_keys to be clicked.

So, an item is selected in listbindings, which activates
button_new_keys, and clicking button_new_keys calls function
Expand Down Expand Up @@ -1221,9 +1221,12 @@ def create_page_keys(self):
scroll_target_y = Scrollbar(frame_target)
scroll_target_x = Scrollbar(frame_target, orient=HORIZONTAL)
self.bindingslist = Listbox(
frame_target, takefocus=FALSE, exportselection=FALSE)
frame_target, takefocus=True, exportselection=FALSE)
self.bindingslist.bind('<ButtonRelease-1>',
self.on_bindingslist_select)
self.bindingslist.bind('<KeyRelease-Up>', self.on_bindingslist_select)
self.bindingslist.bind('<KeyRelease-Down>',
self.on_bindingslist_select)
scroll_target_y['command'] = self.bindingslist.yview
scroll_target_x['command'] = self.bindingslist.xview
self.bindingslist['yscrollcommand'] = scroll_target_y.set
Expand Down Expand Up @@ -1427,7 +1430,14 @@ def save_as_new_key_set(self):
self.create_new_key_set(new_keys_name)

def on_bindingslist_select(self, event):
"Activate button to assign new keys to selected action."
"""Activate button to assign new keys to selected action.

Event can result from either mouse click or Up or Down key.
The keys move the selection, but not the anchor used by
get_new_keys and var_changed_keybinding.
"""
if event.type.name == 'KeyRelease':
self.bindingslist.selection_anchor(ACTIVE)
self.button_new_keys.state(('!disabled',))

def create_new_key_set(self, new_key_set_name):
Expand Down Expand Up @@ -1465,9 +1475,8 @@ def load_keys_list(self, keyset_name):

An action/key binding can be selected to change the key binding.
"""
reselect = False
list_index = 0
if self.bindingslist.curselection():
reselect = True
list_index = self.bindingslist.index(ANCHOR)
keyset = idleConf.GetKeySet(keyset_name)
# 'set' is dict mapping virtual event to list of key events.
Expand All @@ -1482,10 +1491,11 @@ def load_keys_list(self, keyset_name):
if bind_name in changes['keys'][keyset_name]:
key = changes['keys'][keyset_name][bind_name]
self.bindingslist.insert(END, bind_name+' - '+key)
if reselect:
self.bindingslist.see(list_index)
self.bindingslist.select_set(list_index)
self.bindingslist.select_anchor(list_index)
self.bindingslist.see(list_index)
self.bindingslist.select_set(list_index)
self.bindingslist.select_anchor(list_index)
self.bindingslist.activate(list_index)
self.button_new_keys.state(('!disabled',))

@staticmethod
def save_new_key_set(keyset_name, keyset):
Expand Down Expand Up @@ -2124,6 +2134,8 @@ def create_frame_help(self):
scroll_helplist['command'] = self.helplist.yview
self.helplist['yscrollcommand'] = scroll_helplist.set
self.helplist.bind('<ButtonRelease-1>', self.help_source_selected)
self.helplist.bind('<KeyRelease-Up>', self.help_source_selected)
self.helplist.bind('<KeyRelease-Down>', self.help_source_selected)

frame_buttons = Frame(self)
self.button_helplist_edit = Button(
Expand All @@ -2146,7 +2158,14 @@ def create_frame_help(self):
self.button_helplist_remove.pack(side=TOP, anchor=W, pady=5)

def help_source_selected(self, event):
"Handle event for selecting additional help."
"""Handle event for selecting additional help.

Event can result from either mouse click or Up or Down key.
The keys move the selection, but not the anchor used by
helplist_item_edit and helplist_item_remove.
"""
if event.type.name == 'KeyRelease':
self.helplist.selection_anchor(ACTIVE)
self.set_add_delete_state()

def set_add_delete_state(self):
Expand Down
41 changes: 37 additions & 4 deletions Lib/idlelib/idle_test/test_configdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import unittest
from unittest import mock
from idlelib.idle_test.mock_idle import Func
from tkinter import (Tk, StringVar, IntVar, BooleanVar, DISABLED, NORMAL)
from tkinter import (Tk, StringVar, IntVar, BooleanVar, DISABLED, NORMAL,
EventType)
from types import SimpleNamespace
from idlelib import config
from idlelib.configdialog import idleConf, changes, tracers

Expand Down Expand Up @@ -1060,6 +1062,14 @@ def test_on_bindingslist_select(self):
self.assertEqual(b.get('anchor'), 'find')
self.assertNotIn('disabled', d.button_new_keys.state())

# gh-75234: Up and Down keys move the active item, but not the
# anchor; the handler moves the anchor.
d.button_new_keys.state(('disabled',))
b.activate(0)
d.on_bindingslist_select(SimpleNamespace(type=EventType.KeyRelease))
self.assertEqual(b.get('anchor'), 'copy')
self.assertNotIn('disabled', d.button_new_keys.state())

def test_create_new_key_set_and_save_new_key_set(self):
eq = self.assertEqual
d = self.page
Expand Down Expand Up @@ -1110,11 +1120,14 @@ def test_load_keys_list(self):
'force-open-completions - <Control-Key-space>',
'spam - <Shift-Key-a>')

# No current selection.
# No current selection: select the first item.
d.button_new_keys.state(('disabled',))
d.load_keys_list('my keys')
eq(b.get(0, 'end'), expected)
eq(b.get('anchor'), '')
eq(b.curselection(), ())
eq(b.get('anchor'), 'copy - <Control-Key-c> <Control-Key-C>')
eq(b.curselection(), (0, ))
eq(b.index('active'), 0)
self.assertNotIn('disabled', d.button_new_keys.state())

# Check selection.
b.selection_set(1)
Expand Down Expand Up @@ -1584,6 +1597,26 @@ def test_helplist_item_remove(self):
eq(fr.user_helplist, [])
self.assertTrue(fr.upc.called == fr.set.called == 1)

def test_helplist_item_remove_keyboard_selection(self):
# gh-75234: Up and Down keys move the active item, but not the
# anchor; the handler moves the anchor.
eq = self.assertEqual
fr = self.frame
fr.helplist.delete(0, 'end')
fr.helplist.insert('end', 'name1', 'name2')
fr.helplist.selection_anchor(0)
fr.helplist.selection_set(1)
fr.helplist.activate(1)
fr.user_helplist.clear()
fr.user_helplist.extend([('name1', 'file1'), ('name2', 'file2')])
fr.set.called = fr.upc.called = 0

fr.help_source_selected(SimpleNamespace(type=EventType.KeyRelease))
eq(fr.helplist.get('anchor'), 'name2')
fr.helplist_item_remove()
eq(fr.helplist.get(0, 'end'), ('name1',))
eq(fr.user_helplist, [('name1', 'file1')])

def test_update_help_changes(self):
fr = self.frame
self.addCleanup(setattr, fr, 'update_help_changes', Func()) # Re-mask method.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix editing help sources and key bindings in the IDLE Settings dialog after
selecting them with the keyboard.
Loading