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) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits