[tor-commits] [arm/master] Support long top menus on smaller screens, code review fixes.

atagar at torproject.org atagar at torproject.org
Sat Jun 4 21:12:30 UTC 2011


commit fd920dfd4fc88d14b7e569b2513b449cc63cae99
Author: Kamran Riaz Khan <krkhan at inspirated.com>
Date:   Sat May 28 18:29:15 2011 +0500

    Support long top menus on smaller screens, code review fixes.
    
    The top-menu now displays "remaining" entries when right/left arrow keys
    are pressed enough times on shorter screens.
    
    Menus are no longer persistence and therefore are removed from
    Controller's init.
    
    Code review fixes:
    * Menu.draw() is renamed to Menu.showMenu().
    * Overzealous map()s are replaced in favor of list comprehensions.
    * Popups are drawn through popup.add*() methods instead of drawing
      to windows directly.
---
 src/cli/controller.py |   12 ++-----
 src/cli/menu.py       |   78 +++++++++++++++++++++++++++++++++++--------------
 2 files changed, 60 insertions(+), 30 deletions(-)

diff --git a/src/cli/controller.py b/src/cli/controller.py
index c506302..25c3051 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -81,7 +81,7 @@ def initController(stdscr, startTime):
   menu = cli.menu.Menu()
   
   # initializes the controller
-  ARM_CONTROLLER = Controller(stdscr, stickyPanels, pagePanels, menu)
+  ARM_CONTROLLER = Controller(stdscr, stickyPanels, pagePanels)
   
   # additional configuration for the graph panel
   graphPanel = ARM_CONTROLLER.getPanel("graph")
@@ -135,7 +135,7 @@ class Controller:
   Tracks the global state of the interface
   """
   
-  def __init__(self, stdscr, stickyPanels, pagePanels, menu):
+  def __init__(self, stdscr, stickyPanels, pagePanels):
     """
     Creates a new controller instance. Panel lists are ordered as they appear,
     top to bottom on the page.
@@ -150,7 +150,6 @@ class Controller:
     self._screen = stdscr
     self._stickyPanels = stickyPanels
     self._pagePanels = pagePanels
-    self._menu = menu
     self._page = 0
     self._isPaused = False
     self._forceRedraw = False
@@ -188,9 +187,6 @@ class Controller:
     self._forceRedraw = True
     self.setMsg()
   
-  def getMenu(self):
-    return self._menu
-  
   def isPaused(self):
     """
     True if the interface is paused, false otherwise.
@@ -517,8 +513,8 @@ def drawTorMonitor(stdscr, startTime):
     elif key == ord('p') or key == ord('P'):
       control.setPaused(not control.isPaused())
     elif key == ord('m') or key == ord('M'):
-      menu = control.getMenu()
-      menu.draw()
+      menu = cli.menu.Menu()
+      menu.showMenu()
     elif key == ord('q') or key == ord('Q'):
       # provides prompt to confirm that arm should exit
       if CONFIG["features.confirmQuit"]:
diff --git a/src/cli/menu.py b/src/cli/menu.py
index 0395abb..bedb98e 100644
--- a/src/cli/menu.py
+++ b/src/cli/menu.py
@@ -4,7 +4,6 @@ A drop-down menu for sending actions to panels.
 
 import curses
 from collections import namedtuple
-from operator import attrgetter
 
 import cli.controller
 import popups
@@ -25,16 +24,22 @@ class Menu():
       LeafEntry(title="Connections"   , callback=self._callbackDefault),
       LeafEntry(title="Configuration" , callback=self._callbackDefault)))
 
+    self._first = [0]
     self._selection = [0]
-    self._rootEntry = (entry and isinstance(entry, ParentEntry)) and entry or DEFAULT_ROOT
 
-  def draw(self):
+    if entry and isinstance(entry, ParentEntry):
+      self._rootEntry = entry
+    else:
+      self._rootEntry = DEFAULT_ROOT
+
+  def showMenu(self):
     popup, width, height = popups.init(height=3)
     if popup:
       try:
-        popup.win.box()
-
         while True:
+          popup.win.erase()
+          popup.win.box()
+
           self._drawTopLevel(popup, width, height)
 
           popup.win.refresh()
@@ -44,12 +49,10 @@ class Menu():
 
           if key == curses.KEY_RIGHT:
             if len(self._selection) == 1:
-              # selection is on top menu
-              self._selection[0] = (self._selection[0] + 1) % len(self._rootEntry.children)
+              self._moveTopLevelRight(width)
           elif key == curses.KEY_LEFT:
             if len(self._selection) == 1:
-              # selection is on top menu
-              self._selection[0] = (self._selection[0] - 1) % len(self._rootEntry.children)
+              self._moveTopLevelLeft(width)
           elif uiTools.isSelectionKey(key):
             self._handleEvent()
             break
@@ -57,40 +60,71 @@ class Menu():
       finally:
         popups.finalize()
 
-  def _drawTopLevel(self, popup, width, height):
-    titles = map(attrgetter('title'), self._rootEntry.children)
+  def _calculateTopLevelWidths(self, width):
+    titles = [menuItem.title for menuItem in self._rootEntry.children]
 
     # width per title is set according to the longest title
-    titlewidth = max(map(lambda title: len(title), titles)) + 2
+    titlewidth = max(map(len, titles)) + 2
 
     # total number of titles that can be printed in current width
-    printable = width / titlewidth - 1
+    printable = min(width / titlewidth - 1, len(self._rootEntry.children))
+
+    return (titlewidth, printable)
+
+  def _moveTopLevelRight(self, width):
+    _, printable = self._calculateTopLevelWidths(width)
+
+    if self._selection[0] < printable - 1:
+      self._selection[0] = self._selection[0] + 1
+    else:
+      self._selection[0] = 0
+      if printable < len(self._rootEntry.children):
+        self._first[0] = (self._first[0] + printable) % len(self._rootEntry.children)
+
+    if self._first[0] + self._selection[0] == len(self._rootEntry.children):
+      self._first[0] = 0
+      self._selection[0] = 0
+
+  def _moveTopLevelLeft(self, width):
+    _, printable = self._calculateTopLevelWidths(width)
+
+    if self._selection[0] > 0:
+      self._selection[0] = self._selection[0] - 1
+    else:
+      self._first[0] = abs(self._first[0] - printable) % len(self._rootEntry.children)
+      self._selection[0] = len(self._rootEntry.children) - self._first[0] - 1
+
+    if self._selection[0] > printable:
+      self._selection[0] = printable - 1
+
+  def _drawTopLevel(self, popup, width, height):
+    titlewidth, printable = self._calculateTopLevelWidths(width)
+    children = self._rootEntry.children[self._first[0]:self._first[0] + printable]
 
     top = 1
     left = 1
-    for (index, entry) in enumerate(self._rootEntry.children[:printable]):
-      titleformat = curses.A_NORMAL
-
-      if index == self._selection[0]:
-        titleformat = curses.A_STANDOUT
+    for (index, entry) in enumerate(children):
+      titleformat = curses.A_STANDOUT if index == self._selection[0] else curses.A_NORMAL
 
-      popup.win.addch(top, left, curses.ACS_VLINE)
+      popup.addch(top, left, curses.ACS_VLINE)
       left = left + 1
-      popup.win.addstr(top, left, entry.title.center(titlewidth), titleformat)
+      popup.addstr(top, left, entry.title.center(titlewidth), titleformat)
       left = left + titlewidth
 
-    popup.win.addch(top, left, curses.ACS_VLINE)
+    popup.addch(top, left, curses.ACS_VLINE)
     left = left + 1
 
   def _handleEvent(self):
     entry = self._rootEntry
+    sums = [sum(values) for values in zip(self._first, self._selection)]
 
-    for index in self._selection:
+    for index in sums:
       if isinstance(entry, ParentEntry):
         entry = entry.children[index]
       else:
         break
 
+        log.log(log.ERR, "first: %d" % self._first[0])
     if isinstance(entry, LeafEntry):
       entry.callback(entry)
 





More information about the tor-commits mailing list