[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