Skip to content
This repository has been archived by the owner on Aug 6, 2021. It is now read-only.

Commit

Permalink
TextInput: Add support for marking selected text
Browse files Browse the repository at this point in the history
  • Loading branch information
cs142ta committed Jul 31, 2020
1 parent 32772e0 commit 10885b8
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 4 deletions.
75 changes: 71 additions & 4 deletions zygrader/ui/components.py
Expand Up @@ -748,6 +748,9 @@ def __init__(self, height, width, title, prompt, text, mask=TEXT_NORMAL):
# Set cursor to the location of text
self.cursor_index = len(self.text)

# Set selection marks
self.reset_marks()

# Create a text input
self.text_input = curses.newwin(TextInput.TEXT_HEIGHT, self.text_width,
self.y + self.rows - TextInput.TEXT_HEIGHT - 1,
Expand All @@ -767,19 +770,37 @@ def resize(self, rows, cols):
resize_window(self.text_input, TextInput.TEXT_HEIGHT, self.text_width)

def draw_text_chars(self, row, col, display_text):
attrs = 0

start_select = len(display_text)
end_select = start_select
if self.marks:
start_select = min(self.marks)
end_select = max(self.marks)

index = 0
for char in display_text:
add_str(self.text_input, row, col, char)
if index == start_select:
attrs = curses.A_STANDOUT

add_str(self.text_input, row, col, char, attrs)
col += 1
if col >= self.text_width:
col = 0
row += 1

if index == end_select:
attrs = 0
index += 1

def draw(self):
super().draw_text()
self.text_input.erase()

curses.curs_set(0 if self.marks else 1)

# Draw input prompt
add_str(self.text_input, 0, 0, f"Input:")
add_str(self.text_input, 0, 0, f"Input: marks: {self.marks}")

# Draw text and wrap on end of line
if self.masked:
Expand All @@ -800,11 +821,33 @@ def close(self):
curses.curs_set(0)

def addchar(self, c):
# Insert character at cursor location and move cursor
self.text = self.text[:self.cursor_index] + c + self.text[self.cursor_index:]
before_index = self.cursor_index
after_index = self.cursor_index

# If text selected, use marks
if self.marks:
before_index = min(self.marks)
after_index = max(self.marks) + 1
self.reset_marks()

# Insert character at cursor location
# and set cursor one past insert
self.text = self.text[:before_index] + c + self.text[after_index:]
self.cursor_index = before_index
self.right()

def delselection(self):
left = self.text[:min(self.marks)]
right = self.text[max(self.marks) + 1:]
self.text = left + right
self.cursor_index = min(self.marks)
self.reset_marks()

def delchar(self):
if self.marks:
self.delselection()
return

if self.cursor_index == 0:
return

Expand All @@ -813,24 +856,48 @@ def delchar(self):
self.left()

def delcharforward(self):
if self.marks:
self.delselection()
return

if self.cursor_index == len(self.text):
return

# Remove character just forward of cursor location
self.text = self.text[:self.cursor_index] + self.text[self.cursor_index + 1:]

def _cursor_mover(move_func):
"""Wrap a cursor-moving function without moving marks"""
def wrapped(self, shift_pressed=False):
if shift_pressed:
if not self.marks:
self.marks = [self.cursor_index] * 2
move_func(self)
self.marks[1] = self.cursor_index
else:
move_func(self)
self.reset_marks()
return wrapped

@_cursor_mover
def right(self):
self.cursor_index = min(len(self.text), self.cursor_index + 1)

@_cursor_mover
def left(self):
self.cursor_index = max(0, self.cursor_index - 1)

@_cursor_mover
def cursor_to_beginning(self):
self.cursor_index = 0

@_cursor_mover
def cursor_to_end(self):
self.cursor_index = len(self.text)

def reset_marks(self):
self.marks = []

class Logger(Component):
PADDING = 2

Expand Down
8 changes: 8 additions & 0 deletions zygrader/ui/window.py
Expand Up @@ -630,12 +630,20 @@ def create_text_input(self, title, prompt, text="", mask=components.TextInput.TE
text_input.left()
elif event.type == Event.RIGHT:
text_input.right()
elif event.type == Event.SLEFT:
text_input.left(shift_pressed=True)
elif event.type == Event.SRIGHT:
text_input.right(shift_pressed=True)
elif event.type == Event.ESC: # Always allow exiting from text input with ESC
break
elif event.type == Event.HOME:
text_input.cursor_to_beginning()
elif event.type == Event.END:
text_input.cursor_to_end()
elif event.type == Event.SHOME:
text_input.cursor_to_beginning(shift_pressed=True)
elif event.type == Event.SEND:
text_input.cursor_to_end(shift_pressed=True)

self.draw()

Expand Down

0 comments on commit 10885b8

Please sign in to comment.