forked from OSchip/llvm-project
Fixed up some first responder issues and added menubar support that isn't in use yet.
llvm-svn: 249600
This commit is contained in:
parent
d1ac50ecaa
commit
37191a2c2d
|
@ -29,7 +29,6 @@ class Curses(test_results.ResultsFormatter):
|
|||
self.jobs = [None] * 64
|
||||
self.job_tests = [None] * 64
|
||||
self.results = list()
|
||||
self.saved_first_responder = None
|
||||
try:
|
||||
self.main_window = lldbcurses.intialize_curses()
|
||||
self.main_window.add_key_action('\t', self.main_window.select_next_first_responder, "Switch between views that can respond to keyboard input")
|
||||
|
@ -82,8 +81,7 @@ class Curses(test_results.ResultsFormatter):
|
|||
else:
|
||||
self.info_panel.show()
|
||||
|
||||
self.saved_first_responder = self.main_window.first_responder
|
||||
self.main_window.set_first_responder(self.info_panel)
|
||||
self.main_window.push_first_responder(self.info_panel)
|
||||
test_start = self.results[selected_idx][0]
|
||||
test_result = self.results[selected_idx][1]
|
||||
self.info_panel.set_line(0, "File: %s" % (test_start['test_filename']))
|
||||
|
@ -92,9 +90,8 @@ class Curses(test_results.ResultsFormatter):
|
|||
self.info_panel.set_line(3, "Status: %s" % (test_result['status']))
|
||||
|
||||
def hide_info_panel(self):
|
||||
self.info_panel.resign_first_responder(remove_from_parent=True, new_first_responder=self.saved_first_responder)
|
||||
self.main_window.pop_first_responder(self.info_panel)
|
||||
self.info_panel.hide()
|
||||
self.saved_first_responder = None
|
||||
self.main_window.refresh()
|
||||
|
||||
def toggle_status(self, status):
|
||||
|
|
|
@ -63,7 +63,7 @@ class Window(object):
|
|||
self.parent = None
|
||||
self.delegate = delegate
|
||||
self.children = list()
|
||||
self.first_responder = None
|
||||
self.first_responders = list()
|
||||
self.can_become_first_responder = can_become_first_responder
|
||||
self.key_actions = dict()
|
||||
|
||||
|
@ -92,56 +92,106 @@ class Window(object):
|
|||
|
||||
def remove_child(self, window):
|
||||
self.children.remove(window)
|
||||
|
||||
|
||||
def get_first_responder(self):
|
||||
if len(self.first_responders):
|
||||
return self.first_responders[-1]
|
||||
else:
|
||||
return None
|
||||
|
||||
def set_first_responder(self, window):
|
||||
if window.can_become_first_responder:
|
||||
if callable(getattr(window, "hidden", None)) and window.hidden():
|
||||
return False
|
||||
if not window in self.children:
|
||||
self.add_child(window)
|
||||
self.first_responder = window
|
||||
# See if we have a current first responder, and if we do, let it know that
|
||||
# it will be resigning as first responder
|
||||
first_responder = self.get_first_responder()
|
||||
if first_responder:
|
||||
first_responder.relinquish_first_responder()
|
||||
# Now set the first responder to "window"
|
||||
if len(self.first_responders) == 0:
|
||||
self.first_responders.append(window)
|
||||
else:
|
||||
self.first_responders[-1] = window
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def resign_first_responder(self, remove_from_parent, new_first_responder):
|
||||
success = False
|
||||
if self.parent:
|
||||
if self.is_first_responder():
|
||||
self.parent.first_responder = None
|
||||
success = True
|
||||
if remove_from_parent:
|
||||
self.parent.remove_child(self)
|
||||
if new_first_responder:
|
||||
self.parent.set_first_responder(new_first_responder)
|
||||
else:
|
||||
self.parent.select_next_first_responder()
|
||||
return success
|
||||
def push_first_responder(self, window):
|
||||
# Only push the window as the new first responder if the window isn't already the first responder
|
||||
if window != self.get_first_responder():
|
||||
self.first_responders.append(window)
|
||||
|
||||
def pop_first_responder(self, window):
|
||||
# Only pop the window from the first responder list if it is the first responder
|
||||
if window == self.get_first_responder():
|
||||
old_first_responder = self.first_responders.pop()
|
||||
old_first_responder.relinquish_first_responder()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def relinquish_first_responder(self):
|
||||
'''Override if there is something that you need to do when you lose first responder status.'''
|
||||
pass
|
||||
|
||||
# def resign_first_responder(self, remove_from_parent, new_first_responder):
|
||||
# success = False
|
||||
# if self.parent:
|
||||
# if self.is_first_responder():
|
||||
# self.relinquish_first_responder()
|
||||
# if len(self.parent.first_responder):
|
||||
# self.parent.first_responder = None
|
||||
# success = True
|
||||
# if remove_from_parent:
|
||||
# self.parent.remove_child(self)
|
||||
# if new_first_responder:
|
||||
# self.parent.set_first_responder(new_first_responder)
|
||||
# else:
|
||||
# self.parent.select_next_first_responder()
|
||||
# return success
|
||||
|
||||
def is_first_responder(self):
|
||||
if self.parent:
|
||||
return self.parent.first_responder == self
|
||||
return self.parent.get_first_responder() == self
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_in_first_responder_chain(self):
|
||||
if self.parent:
|
||||
return self in self.parent.first_responders
|
||||
else:
|
||||
return False
|
||||
|
||||
def select_next_first_responder(self):
|
||||
num_children = len(self.children)
|
||||
if num_children == 1:
|
||||
return self.set_first_responder(self.children[0])
|
||||
for (i,window) in enumerate(self.children):
|
||||
if window.is_first_responder():
|
||||
break
|
||||
if i < num_children:
|
||||
for i in range(i+1,num_children):
|
||||
if self.set_first_responder(self.children[i]):
|
||||
return True
|
||||
for i in range(0, i):
|
||||
if self.set_first_responder(self.children[i]):
|
||||
return True
|
||||
if len(self.first_responders) > 1:
|
||||
self.pop_first_responder(self.first_responders[-1])
|
||||
else:
|
||||
num_children = len(self.children)
|
||||
if num_children == 1:
|
||||
return self.set_first_responder(self.children[0])
|
||||
for (i,window) in enumerate(self.children):
|
||||
if window.is_first_responder():
|
||||
break
|
||||
if i < num_children:
|
||||
for i in range(i+1,num_children):
|
||||
if self.set_first_responder(self.children[i]):
|
||||
return True
|
||||
for i in range(0, i):
|
||||
if self.set_first_responder(self.children[i]):
|
||||
return True
|
||||
|
||||
def point_in_window(self, pt):
|
||||
size = self.get_size()
|
||||
return pt.x >= 0 and pt.x < size.w and pt.y >= 0 and pt.y < size.h
|
||||
|
||||
def addch(self, pt, c):
|
||||
try:
|
||||
self.window.addch(pt.y, pt.x, c)
|
||||
except:
|
||||
pass
|
||||
|
||||
def addstr(self, pt, str):
|
||||
try:
|
||||
|
@ -198,7 +248,7 @@ class Window(object):
|
|||
def get_size(self):
|
||||
(y, x) = self.window.getmaxyx()
|
||||
return Size(w=x, h=y)
|
||||
|
||||
|
||||
def refresh(self):
|
||||
self.update()
|
||||
curses.panel.update_panels()
|
||||
|
@ -216,8 +266,8 @@ class Window(object):
|
|||
# First try the first responder if this window has one, but don't allow
|
||||
# it to check with its parent (False second parameter) so we don't recurse
|
||||
# and get a stack overflow
|
||||
if self.first_responder:
|
||||
if self.first_responder.handle_key(key, False):
|
||||
for first_responder in reversed(self.first_responders):
|
||||
if first_responder.handle_key(key, False):
|
||||
return True
|
||||
|
||||
# Check our key map to see if we have any actions. Actions don't take
|
||||
|
@ -263,7 +313,10 @@ class Window(object):
|
|||
while n > 0:
|
||||
c = self.window.getch()
|
||||
if c != -1:
|
||||
self.handle_key(c)
|
||||
try:
|
||||
self.handle_key(c)
|
||||
except:
|
||||
break
|
||||
n -= 1
|
||||
|
||||
class Panel(Window):
|
||||
|
@ -300,6 +353,12 @@ class BoxedPanel(Panel):
|
|||
self.lines = list()
|
||||
self.first_visible_idx = 0
|
||||
self.selected_idx = -1
|
||||
self.add_key_action(curses.KEY_UP, self.select_prev, "Select the previous item")
|
||||
self.add_key_action(curses.KEY_DOWN, self.select_next, "Select the next item")
|
||||
self.add_key_action(curses.KEY_HOME, self.scroll_begin, "Go to the beginning of the list")
|
||||
self.add_key_action(curses.KEY_END, self.scroll_end, "Go to the end of the list")
|
||||
self.add_key_action(curses.KEY_PPAGE, self.scroll_page_backward, "Scroll to previous page")
|
||||
self.add_key_action(curses.KEY_NPAGE, self.scroll_page_forward, "Scroll to next forward")
|
||||
self.update()
|
||||
|
||||
def clear(self, update=True):
|
||||
|
@ -357,12 +416,28 @@ class BoxedPanel(Panel):
|
|||
self.first_visible_idx = 0
|
||||
self.selected_idx = num_lines-1
|
||||
self.update()
|
||||
|
||||
|
||||
def scroll_page_backward(self):
|
||||
num_lines = len(self.lines)
|
||||
max_visible_lines = self.get_usable_height()
|
||||
new_index = self.first_visible_idx - max_visible_lines
|
||||
if new_index < 0:
|
||||
self.first_visible_idx = 0
|
||||
else:
|
||||
self.first_visible_idx = new_index
|
||||
self.refresh()
|
||||
|
||||
def scroll_page_forward(self):
|
||||
max_visible_lines = self.get_usable_height()
|
||||
self.first_visible_idx += max_visible_lines
|
||||
self._adjust_first_visible_line()
|
||||
self.refresh()
|
||||
|
||||
def select_next (self):
|
||||
self.selected_idx += 1
|
||||
if self.selected_idx >= len(self.lines):
|
||||
self.selected_idx = len(self.lines) - 1
|
||||
self.update()
|
||||
self.refresh()
|
||||
|
||||
def select_prev (self):
|
||||
self.selected_idx -= 1
|
||||
|
@ -371,7 +446,7 @@ class BoxedPanel(Panel):
|
|||
self.selected_idx = 0
|
||||
else:
|
||||
self.selected_idx = -1
|
||||
self.update()
|
||||
self.refresh()
|
||||
|
||||
def get_selected_idx(self):
|
||||
return self.selected_idx
|
||||
|
@ -379,7 +454,7 @@ class BoxedPanel(Panel):
|
|||
def _adjust_first_visible_line(self):
|
||||
num_lines = len(self.lines)
|
||||
max_visible_lines = self.get_usable_height()
|
||||
if (num_lines - self.first_visible_idx) > max_visible_lines:
|
||||
if (self.first_visible_idx >= num_lines) or (num_lines - self.first_visible_idx) > max_visible_lines:
|
||||
self.first_visible_idx = num_lines - max_visible_lines
|
||||
|
||||
def append_line(self, s, update=True):
|
||||
|
@ -401,11 +476,11 @@ class BoxedPanel(Panel):
|
|||
|
||||
def update(self):
|
||||
self.erase()
|
||||
is_first_responder = self.is_first_responder()
|
||||
if is_first_responder:
|
||||
is_in_first_responder_chain = self.is_in_first_responder_chain()
|
||||
if is_in_first_responder_chain:
|
||||
self.attron (curses.A_REVERSE)
|
||||
self.box()
|
||||
if is_first_responder:
|
||||
if is_in_first_responder_chain:
|
||||
self.attroff (curses.A_REVERSE)
|
||||
if self.title:
|
||||
self.addstr(Point(x=2, y=0), ' ' + self.title + ' ')
|
||||
|
@ -422,6 +497,150 @@ class BoxedPanel(Panel):
|
|||
else:
|
||||
return
|
||||
|
||||
class Item(object):
|
||||
def __init__(self, title, action):
|
||||
self.title = title
|
||||
self.action = action
|
||||
|
||||
class Menu(BoxedPanel):
|
||||
def __init__(self, title, items):
|
||||
max_title_width = 0
|
||||
for item in items:
|
||||
if max_title_width < len(item.title):
|
||||
max_title_width = len(item.title)
|
||||
frame = Rect(x=0, y=0, w=max_title_width+4, h=len(items)+2)
|
||||
super(Menu, self).__init__(frame, title=None, delegate=None, can_become_first_responder=True)
|
||||
self.selected_idx = 0
|
||||
self.title = title
|
||||
self.items = items
|
||||
for (item_idx, item) in enumerate(items):
|
||||
self.set_line(item_idx, item.title)
|
||||
self.hide()
|
||||
|
||||
def update(self):
|
||||
super(Menu, self).update()
|
||||
|
||||
def relinquish_first_responder(self):
|
||||
if not self.hidden():
|
||||
self.hide()
|
||||
|
||||
def perform_action(self):
|
||||
selected_idx = self.get_selected_idx()
|
||||
if selected_idx < len(self.items):
|
||||
action = self.items[selected_idx].action
|
||||
if action:
|
||||
action()
|
||||
|
||||
class MenuBar(Panel):
|
||||
def __init__(self, frame):
|
||||
super(MenuBar, self).__init__(frame, can_become_first_responder=True)
|
||||
self.menus = list()
|
||||
self.selected_menu_idx = -1
|
||||
self.add_key_action(curses.KEY_LEFT, self.select_prev, "Select the previous menu")
|
||||
self.add_key_action(curses.KEY_RIGHT, self.select_next, "Select the next menu")
|
||||
self.add_key_action(curses.KEY_DOWN, lambda: self.select(0), "Select the first menu")
|
||||
self.add_key_action(27, self.relinquish_first_responder, "Hide current menu")
|
||||
self.add_key_action(curses.KEY_ENTER, self.perform_action, "Select the next menu item")
|
||||
self.add_key_action(10, self.perform_action, "Select the next menu item")
|
||||
|
||||
def insert_menu(self, menu, index=sys.maxint):
|
||||
if index >= len(self.menus):
|
||||
self.menus.append(menu)
|
||||
else:
|
||||
self.menus.insert(index, menu)
|
||||
pt = self.get_position()
|
||||
for menu in self.menus:
|
||||
menu.set_position(pt)
|
||||
pt.x += len(menu.title) + 5
|
||||
|
||||
def perform_action(self):
|
||||
'''If no menu is visible, show the first menu. If a menu is visible, perform the action
|
||||
associated with the selected menu item in the menu'''
|
||||
menu_visible = False
|
||||
for menu in self.menus:
|
||||
if not menu.hidden():
|
||||
menu_visible = True
|
||||
break
|
||||
if menu_visible:
|
||||
menu.perform_action()
|
||||
self.selected_menu_idx = -1
|
||||
self._selected_menu_changed()
|
||||
else:
|
||||
self.select(0)
|
||||
|
||||
def relinquish_first_responder(self):
|
||||
if self.selected_menu_idx >= 0:
|
||||
self.selected_menu_idx = -1
|
||||
self._selected_menu_changed()
|
||||
|
||||
def _selected_menu_changed(self):
|
||||
for (menu_idx, menu) in enumerate(self.menus):
|
||||
is_hidden = menu.hidden()
|
||||
if menu_idx != self.selected_menu_idx:
|
||||
if not is_hidden:
|
||||
if self.parent.pop_first_responder(menu) == False:
|
||||
menu.hide()
|
||||
for (menu_idx, menu) in enumerate(self.menus):
|
||||
is_hidden = menu.hidden()
|
||||
if menu_idx == self.selected_menu_idx:
|
||||
if is_hidden:
|
||||
menu.show()
|
||||
self.parent.push_first_responder(menu)
|
||||
menu.top()
|
||||
self.parent.refresh()
|
||||
|
||||
def select(self, index):
|
||||
if index < len(self.menus):
|
||||
self.selected_menu_idx = index
|
||||
self._selected_menu_changed()
|
||||
|
||||
def select_next (self):
|
||||
num_menus = len(self.menus)
|
||||
if self.selected_menu_idx == -1:
|
||||
if num_menus > 0:
|
||||
self.selected_menu_idx = 0
|
||||
self._selected_menu_changed()
|
||||
else:
|
||||
if self.selected_menu_idx + 1 < num_menus:
|
||||
self.selected_menu_idx += 1
|
||||
else:
|
||||
self.selected_menu_idx = -1
|
||||
self._selected_menu_changed()
|
||||
|
||||
def select_prev (self):
|
||||
num_menus = len(self.menus)
|
||||
if self.selected_menu_idx == -1:
|
||||
if num_menus > 0:
|
||||
self.selected_menu_idx = num_menus - 1
|
||||
self._selected_menu_changed()
|
||||
else:
|
||||
if self.selected_menu_idx - 1 >= 0:
|
||||
self.selected_menu_idx -= 1
|
||||
else:
|
||||
self.selected_menu_idx = -1
|
||||
self._selected_menu_changed()
|
||||
|
||||
def update(self):
|
||||
self.erase()
|
||||
is_in_first_responder_chain = self.is_in_first_responder_chain()
|
||||
if is_in_first_responder_chain:
|
||||
self.attron (curses.A_REVERSE)
|
||||
pt = Point(x=0, y=0)
|
||||
for menu in self.menus:
|
||||
self.addstr(pt, '| ' + menu.title + ' ')
|
||||
pt.x += len(menu.title) + 5
|
||||
self.addstr(pt, '|')
|
||||
width = self.get_size().w
|
||||
while pt.x < width:
|
||||
self.addch(pt, ' ')
|
||||
pt.x += 1
|
||||
if is_in_first_responder_chain:
|
||||
self.attroff (curses.A_REVERSE)
|
||||
|
||||
for menu in self.menus:
|
||||
menu.update()
|
||||
|
||||
|
||||
class StatusPanel(Panel):
|
||||
def __init__(self, frame):
|
||||
super(StatusPanel, self).__init__(frame, delegate=None, can_become_first_responder=False)
|
||||
|
|
Loading…
Reference in New Issue