[Lldb-commits] [lldb] r249600 - Fixed up some first responder issues and added menubar support that isn't in use yet.
Greg Clayton via lldb-commits
lldb-commits at lists.llvm.org
Wed Oct 7 13:00:29 PDT 2015
Author: gclayton
Date: Wed Oct 7 15:00:28 2015
New Revision: 249600
URL: http://llvm.org/viewvc/llvm-project?rev=249600&view=rev
Log:
Fixed up some first responder issues and added menubar support that isn't in use yet.
Modified:
lldb/trunk/test/curses_results.py
lldb/trunk/test/lldbcurses.py
Modified: lldb/trunk/test/curses_results.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/curses_results.py?rev=249600&r1=249599&r2=249600&view=diff
==============================================================================
--- lldb/trunk/test/curses_results.py (original)
+++ lldb/trunk/test/curses_results.py Wed Oct 7 15:00:28 2015
@@ -29,7 +29,6 @@ class Curses(test_results.ResultsFormatt
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.ResultsFormatt
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.ResultsFormatt
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):
Modified: lldb/trunk/test/lldbcurses.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lldbcurses.py?rev=249600&r1=249599&r2=249600&view=diff
==============================================================================
--- lldb/trunk/test/lldbcurses.py (original)
+++ lldb/trunk/test/lldbcurses.py Wed Oct 7 15:00:28 2015
@@ -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)
More information about the lldb-commits
mailing list