Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding descriptions for each option #114

Merged
merged 23 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4899b48
Update __init__.py
iamalisalehi Mar 26, 2024
912da47
An example to show descriptions.
iamalisalehi Mar 26, 2024
82a75ea
Update __init__.py
iamalisalehi Mar 26, 2024
71ab424
1) changed the main program to parse dictionaries for options with de…
iamalisalehi Mar 28, 2024
58dc2e7
Delete .idea directory
iamalisalehi Mar 31, 2024
d05493f
Merge branch 'master' into master
iamalisalehi Apr 1, 2024
678c7c0
added optional attribute to the class
iamalisalehi Apr 1, 2024
2819f15
add to
iamalisalehi Apr 1, 2024
1103de8
trying to fix some commit messages
iamalisalehi Apr 1, 2024
0fbcfdf
Delete .idea directory
iamalisalehi Apr 1, 2024
95acccb
Fixed types and refactored examples
iamalisalehi Apr 2, 2024
cffdf81
changed test_option()
iamalisalehi Apr 2, 2024
187f047
fixed type checks for the eddited test
iamalisalehi Apr 2, 2024
f327cc3
revert back type hints grammer
iamalisalehi Apr 2, 2024
d501f20
fixed an error caused ny find/replace all
iamalisalehi Apr 2, 2024
388969c
Applied early return principle and fixed some minor stuff
iamalisalehi Apr 3, 2024
9098b2c
Resolving widescreen bug
iamalisalehi Apr 4, 2024
fb98b9f
revert back support for dictionary input
iamalisalehi Apr 7, 2024
9d085dc
update examples
iamalisalehi Apr 7, 2024
cb4b44a
update tests
iamalisalehi Apr 7, 2024
b946530
revert List to Sequence for options type
iamalisalehi Apr 7, 2024
be993c5
Update src/pick/__init__.py
iamalisalehi Apr 8, 2024
87540ac
If no option present, use the whole line
iamalisalehi Apr 8, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ __pycache__/
# due to using tox and pytest
.tox
.cache
.idea/
2 changes: 1 addition & 1 deletion example/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
title = "Please choose your favorite programming language: "
options = ["Java", "JavaScript", "Python", "PHP", "C++", "Erlang", "Haskell"]
option, index = pick(options, title, indicator="=>", default_index=2)
print(f"You choosed {option} at index {index}")
print(f"You chose {option} at index {index}")
9 changes: 5 additions & 4 deletions example/option.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

title = "Please choose your favorite programming language: "
options = [
Option("Java", ".java"),
Option("Python", ".py"),
Option("Python", ".py", "Python is a high-level, general-purpose programming language. Its design philosophy emphasizes code readability with the use of significant indentation."),
Option("Java", description="Java is a high-level, class-based, object-oriented programming language that is designed to have as few implementation dependencies as possible. It is a general-purpose programming language intended to let programmers write once, run anywhere (WORA), meaning that compiled Java code can run on all platforms that support Java without the need to recompile."),
Option("JavaScript", ".js"),
Option("C++")
]
option, index = pick(options, title)
print(f"You choosed {option} at index {index}")
option, index = pick(options, title, indicator="=>")
print(f"You chose {option} at index {index}")
45 changes: 40 additions & 5 deletions src/pick/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import curses
from dataclasses import dataclass, field
from typing import Any, List, Optional, Sequence, Tuple, TypeVar, Union, Generic
from typing import Any, Generic, List, Optional, Sequence, Tuple, TypeVar, Union

__all__ = ["Picker", "pick", "Option"]


@dataclass
class Option:
label: str
value: Any
value: Any = None
description: Optional[str] = None


KEYS_ENTER = (curses.KEY_ENTER, ord("\n"), ord("\r"))
Expand All @@ -22,7 +23,6 @@ class Option:
OPTION_T = TypeVar("OPTION_T", str, Option)
PICK_RETURN_T = Tuple[OPTION_T, int]


@dataclass
class Picker(Generic[OPTION_T]):
options: Sequence[OPTION_T]
Expand Down Expand Up @@ -104,13 +104,32 @@ def get_option_lines(self) -> List[str]:

return lines

def get_lines(self) -> Tuple[List, int]:
def get_lines(self) -> Tuple[List[str], int]:
title_lines = self.get_title_lines()
option_lines = self.get_option_lines()
lines = title_lines + option_lines
current_line = self.index + len(title_lines) + 1
return lines, current_line

def get_description_lines(self, description: str, length: int) -> List[str]:
description_words = description.split(" ")
description_lines: List[str] = []

line = ""
for i, word in enumerate(description_words):
if len(line + " " + word) <= length:
if i == 0:
line += word
else:
line += " " + word
else:
description_lines.append(line)
line = word

description_lines.append(line)

return description_lines

def draw(self, screen: "curses._CursesWindow") -> None:
"""draw the curses ui on the screen, handle scroll if needed"""
screen.clear()
Expand All @@ -128,10 +147,26 @@ def draw(self, screen: "curses._CursesWindow") -> None:

lines_to_draw = lines[scroll_top : scroll_top + max_rows]

description_present = False
for option in self.options:
if isinstance(option, str) or option.description is not None:
description_present = True
break

for line in lines_to_draw:
screen.addnstr(y, x, line, max_x - 2)
if description_present:
screen.addnstr(y, x, line, max_x // 2 - 2)
else:
screen.addnstr(y, x, line, max_x - 2)
y += 1

option = self.options[self.index]
if isinstance(option, Option) and option.description is not None:
description_lines = self.get_description_lines(option.description, max_x // 2)

for i, line in enumerate(description_lines):
screen.addnstr(i + 3, max_x // 2, line, 2 * max_x // 2 - 2)

screen.refresh()

def run_loop(
Expand Down
20 changes: 13 additions & 7 deletions tests/test_pick.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,16 @@ def test_multi_select():


def test_option():
options = [Option("option1", 101), Option("option2", 102), Option("option3", 103)]
picker = Picker(options)
picker.move_down()
option, index = picker.get_selected()
assert index == 1
assert isinstance(option, Option)
assert option.value == 102
options = [Option("option1", 101, "description1"), Option("option2", 102),
iamalisalehi marked this conversation as resolved.
Show resolved Hide resolved
Option("option3", description="description3"), Option("option4")]
picker = Picker(options, multiselect=True)
for _ in range(4):
picker.mark_index()
picker.move_down()
selected_options = picker.get_selected()
for option in selected_options:
assert isinstance(option[0], Option)
option = selected_options[0]
assert option[0].label == "option1"
assert option[0].value == 101
assert option[0].description == "description1"
Loading