Skip to content

Commit

Permalink
Merge pull request #5361 from Textualize/command-palette-highlight
Browse files Browse the repository at this point in the history
highlight top item
  • Loading branch information
willmcgugan authored Dec 8, 2024
2 parents 2686bba + b609303 commit aef7c9e
Show file tree
Hide file tree
Showing 10 changed files with 468 additions and 491 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Change default quit key to `ctrl+q` https://github.com/Textualize/textual/pull/5352
- Changed delete line binding on TextArea to use `ctrl+shift+x` https://github.com/Textualize/textual/pull/5352
- The command palette will now select the top item automatically https://github.com/Textualize/textual/pull/5361

### Fixed

Expand Down
29 changes: 8 additions & 21 deletions src/textual/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from dataclasses import dataclass
from functools import total_ordering
from inspect import isclass
from operator import attrgetter
from time import monotonic
from typing import (
TYPE_CHECKING,
Expand Down Expand Up @@ -455,9 +456,9 @@ class CommandList(OptionList, can_focus=False):
}
CommandList > .option-list--option-highlighted {
color: $block-cursor-foreground;
background: $block-cursor-background;
text-style: $block-cursor-text-style;
color: $block-cursor-blurred-foreground;
background: $block-cursor-blurred-background;
text-style: $block-cursor-blurred-text-style;
}
CommandList:nocolor > .option-list--option-highlighted {
Expand Down Expand Up @@ -1014,26 +1015,12 @@ def _refresh_command_list(
commands: The commands to show in the widget.
clear_current: Should the current content of the list be cleared first?
"""
# For the moment, this is a fairly naive approach to populating the
# command list with a list of commands. Every time we add a
# new one we're nuking the list of options and populating them
# again. If this turns out to not be a great approach, we may try
# and get a lot smarter with this (ideally OptionList will grow a
# method to sort its content in an efficient way; but for now we'll
# go with "worse is better" wisdom).
highlighted = (
command_list.get_option_at_index(command_list.highlighted)
if command_list.highlighted is not None and not clear_current
else None
)

def sort_key(command: Command) -> float:
return -command.hit.score

sorted_commands = sorted(commands, key=sort_key)
sorted_commands = sorted(commands, key=attrgetter("hit.score"), reverse=True)
command_list.clear_options().add_options(sorted_commands)
if highlighted is not None and highlighted.id:
command_list.highlighted = command_list.get_option_index(highlighted.id)

if sorted_commands:
command_list.highlighted = 0

self._list_visible = bool(command_list.option_count)
self._hit_count = command_list.option_count
Expand Down
27 changes: 7 additions & 20 deletions tests/command_palette/test_interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ def on_mount(self) -> None:


async def test_initial_list_no_highlight() -> None:
"""When the list initially appears, nothing will be highlighted."""
"""When the list initially appears, the first item is highlghted."""
async with CommandPaletteApp().run_test() as pilot:
assert CommandPalette.is_open(pilot.app)
assert pilot.app.screen.query_one(CommandList).visible is False
await pilot.press("a")
assert pilot.app.screen.query_one(CommandList).visible is True
assert pilot.app.screen.query_one(CommandList).highlighted is None
assert pilot.app.screen.query_one(CommandList).highlighted == 0


async def test_down_arrow_selects_an_item() -> None:
Expand All @@ -35,32 +35,19 @@ async def test_down_arrow_selects_an_item() -> None:
assert pilot.app.screen.query_one(CommandList).visible is False
await pilot.press("a")
assert pilot.app.screen.query_one(CommandList).visible is True
assert pilot.app.screen.query_one(CommandList).highlighted is None
assert pilot.app.screen.query_one(CommandList).highlighted == 0
await pilot.press("down")
assert pilot.app.screen.query_one(CommandList).highlighted is not None
assert pilot.app.screen.query_one(CommandList).highlighted == 1


async def test_enter_selects_an_item() -> None:
"""Typing in a search value then pressing enter should select a command."""
"""Typing in a search value then pressing enter should dismiss the command palette."""
async with CommandPaletteApp().run_test() as pilot:
assert CommandPalette.is_open(pilot.app)
assert pilot.app.screen.query_one(CommandList).visible is False
await pilot.press("a")
assert pilot.app.screen.query_one(CommandList).visible is True
assert pilot.app.screen.query_one(CommandList).highlighted is None
await pilot.press("enter")
assert pilot.app.screen.query_one(CommandList).highlighted is not None


async def test_selection_of_command_closes_command_palette() -> None:
"""Selecting a command from the list should close the list."""
async with CommandPaletteApp().run_test() as pilot:
assert CommandPalette.is_open(pilot.app)
assert pilot.app.screen.query_one(CommandList).visible is False
await pilot.press("a")
assert pilot.app.screen.query_one(CommandList).visible is True
assert pilot.app.screen.query_one(CommandList).highlighted is None
await pilot.press("enter")
assert pilot.app.screen.query_one(CommandList).highlighted is not None
assert pilot.app.screen.query_one(CommandList).highlighted == 0
await pilot.press("enter")
assert not CommandPalette.is_open(pilot.app)
assert not pilot.app.screen.query(CommandList)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit aef7c9e

Please sign in to comment.