[or-cvs] r23728: {arm} Cursor selection for entries in the config state. (in arm/trunk/src: . interface util)

Damian Johnson atagar1 at gmail.com
Sun Oct 31 08:13:38 UTC 2010


Author: atagar
Date: 2010-10-31 08:13:38 +0000 (Sun, 31 Oct 2010)
New Revision: 23728

Modified:
   arm/trunk/src/interface/configStatePanel.py
   arm/trunk/src/starter.py
   arm/trunk/src/util/uiTools.py
Log:
Cursor selection for entries in the config state.



Modified: arm/trunk/src/interface/configStatePanel.py
===================================================================
--- arm/trunk/src/interface/configStatePanel.py	2010-10-31 03:44:46 UTC (rev 23727)
+++ arm/trunk/src/interface/configStatePanel.py	2010-10-31 08:13:38 UTC (rev 23728)
@@ -60,7 +60,7 @@
     
     self.configType = configType
     self.confContents = []
-    self.scroll = 0
+    self.scroller = uiTools.Scroller(True)
     self.valsLock = threading.RLock()
     
     # TODO: this will need to be able to listen for SETCONF events (arg!)
@@ -98,11 +98,8 @@
     self.valsLock.acquire()
     if uiTools.isScrollKey(key):
       pageHeight = self.getPreferredSize()[0] - 1
-      newScroll = uiTools.getScrollPosition(key, self.scroll, pageHeight, len(self.confContents))
-      
-      if self.scroll != newScroll:
-        self.scroll = newScroll
-        self.redraw(True)
+      isChanged = self.scroller.handleKey(key, self.confContents, pageHeight)
+      if isChanged: self.redraw(True)
   
   def draw(self, subwindow, width, height):
     self.valsLock.acquire()
@@ -111,11 +108,14 @@
     sourceLabel = "Tor" if self.configType == TOR_STATE else "Arm"
     self.addstr(0, 0, "%s Config:" % sourceLabel, curses.A_STANDOUT)
     
+    scrollLoc = self.scroller.getScrollLoc(self.confContents, height - 1)
+    cursorSelection = self.scroller.getCursorSelection(self.confContents)
+    
     # draws left-hand scroll bar if content's longer than the height
     scrollOffset = 0
     if len(self.confContents) > height - 1:
       scrollOffset = 3
-      self.addScrollBar(self.scroll, self.scroll + height - 1, len(self.confContents), 1)
+      self.addScrollBar(scrollLoc, scrollLoc + height - 1, len(self.confContents), 1)
     
     # determines the width for the columns
     optionColWidth, valueColWidth, typeColWidth = 0, 0, 0
@@ -130,29 +130,24 @@
     
     optionColWidth = min(self._config["features.config.state.colWidth.option"], optionColWidth)
     valueColWidth = min(self._config["features.config.state.colWidth.value"], valueColWidth)
+    descriptionColWidth = max(0, width - scrollOffset - optionColWidth - valueColWidth - typeColWidth - 3)
     
-    for lineNum in range(self.scroll, len(self.confContents)):
+    for lineNum in range(scrollLoc, len(self.confContents)):
       entry = self.confContents[lineNum]
-      drawLine = lineNum + 1 - self.scroll
+      drawLine = lineNum + 1 - scrollLoc
       
+      # TODO: need to cut off description at the first newline
       optionLabel = uiTools.cropStr(entry.option, optionColWidth)
       valueLabel = uiTools.cropStr(entryToValues[entry], valueColWidth)
+      descriptionLabel = uiTools.cropStr(entry.description, descriptionColWidth, None)
       
       lineFormat = uiTools.getColor("green") if entry.isDefault else curses.A_BOLD | uiTools.getColor("yellow")
-      xOffset = scrollOffset
+      if entry == cursorSelection: lineFormat |= curses.A_STANDOUT
       
-      self.addstr(drawLine, xOffset, optionLabel, lineFormat)
-      xOffset += optionColWidth + 1
+      lineTextLayout = "%%-%is %%-%is %%-%is %%-%is" % (optionColWidth, valueColWidth, typeColWidth, descriptionColWidth)
+      lineText = lineTextLayout % (optionLabel, valueLabel, entry.type, descriptionLabel)
+      self.addstr(drawLine, scrollOffset, lineText, lineFormat)
       
-      self.addstr(drawLine, xOffset, valueLabel, lineFormat)
-      xOffset += valueColWidth + 1
-      
-      self.addstr(drawLine, xOffset, entry.type, lineFormat)
-      xOffset += typeColWidth + 1
-      
-      descriptionLabel = uiTools.cropStr(entry.description, width - xOffset)
-      self.addstr(drawLine, xOffset, descriptionLabel, lineFormat)
-      
       if drawLine >= height: break
     
     self.valsLock.release()

Modified: arm/trunk/src/starter.py
===================================================================
--- arm/trunk/src/starter.py	2010-10-31 03:44:46 UTC (rev 23727)
+++ arm/trunk/src/starter.py	2010-10-31 08:13:38 UTC (rev 23728)
@@ -255,7 +255,7 @@
   if conn == None: sys.exit(1)
   
   # initializing the connection may require user input (for the password)
-  # scewing the startup time results so this isn't counted
+  # skewing the startup time results so this isn't counted
   initTime = time.time() - startTime
   controller = util.torTools.getConn()
   controller.init(conn)

Modified: arm/trunk/src/util/uiTools.py
===================================================================
--- arm/trunk/src/util/uiTools.py	2010-10-31 03:44:46 UTC (rev 23727)
+++ arm/trunk/src/util/uiTools.py	2010-10-31 08:13:38 UTC (rev 23728)
@@ -141,7 +141,7 @@
   
   return key in SCROLL_KEYS
 
-def getScrollPosition(key, position, pageHeight, contentHeight):
+def getScrollPosition(key, position, pageHeight, contentHeight, isCursor = False):
   """
   Parses navigation keys, providing the new scroll possition the panel should
   use. Position is always between zero and (contentHeight - pageHeight). This
@@ -158,19 +158,21 @@
     position      - starting position
     pageHeight    - size of a single screen's worth of content
     contentHeight - total lines of content that can be scrolled
+    isCursor      - tracks a cursor position rather than scroll if true
   """
   
   if isScrollKey(key):
     shift = 0
     if key == curses.KEY_UP: shift = -1
     elif key == curses.KEY_DOWN: shift = 1
-    elif key == curses.KEY_PPAGE: shift = -pageHeight
-    elif key == curses.KEY_NPAGE: shift = pageHeight
+    elif key == curses.KEY_PPAGE: shift = -pageHeight + 1 if isCursor else -pageHeight
+    elif key == curses.KEY_NPAGE: shift = pageHeight - 1 if isCursor else pageHeight
     elif key == curses.KEY_HOME: shift = -contentHeight
     elif key == curses.KEY_END: shift = contentHeight
     
     # returns the shift, restricted to valid bounds
-    return max(0, min(position + shift, contentHeight - pageHeight))
+    maxLoc = contentHeight - 1 if isCursor else contentHeight - pageHeight
+    return max(0, min(position + shift, maxLoc))
   else: return position
 
 def getSizeLabel(bytes, decimal = 0, isLong = False, isBytes=True):
@@ -242,6 +244,90 @@
   
   return timeLabels
 
+class Scroller:
+  """
+  Tracks the scrolling position when there might be a visible cursor. This
+  expects that there is a single line displayed per an entry in the contents.
+  """
+  
+  def __init__(self, isCursorEnabled):
+    self.scrollLoc, self.cursorLoc = 0, 0
+    self.cursorSelection = None
+    self.isCursorEnabled = isCursorEnabled
+  
+  def getScrollLoc(self, content, pageHeight):
+    """
+    Provides the scrolling location, taking into account its cursor's location
+    content size, and page height.
+    
+    Arguments:
+      content    - displayed content
+      pageHeight - height of the display area for the content
+    """
+    
+    if content and pageHeight:
+      self.scrollLoc = max(0, min(self.scrollLoc, len(content) - pageHeight + 1))
+      
+      if self.isCursorEnabled:
+        self.getCursorSelection(content) # resets the cursor location
+        
+        if self.cursorLoc < self.scrollLoc:
+          self.scrollLoc = self.cursorLoc
+        elif self.cursorLoc > self.scrollLoc + pageHeight - 1:
+          self.scrollLoc = self.cursorLoc - pageHeight + 1
+    
+    return self.scrollLoc
+  
+  def getCursorSelection(self, content):
+    """
+    Provides the selected item in the content. This is the same entry until
+    the cursor moves or it's no longer available (in which case it moves on to
+    the next entry).
+    
+    Arguments:
+      content - displayed content
+    """
+    
+    # TODO: needs to handle duplicate entries when using this for the
+    # connection panel
+    
+    if not self.isCursorEnabled: return None
+    elif not content:
+      self.cursorLoc, self.cursorSelection = 0, None
+      return None
+    
+    self.cursorLoc = min(self.cursorLoc, len(content) - 1)
+    if self.cursorSelection != None and self.cursorSelection in content:
+      # moves cursor location to track the selection
+      self.cursorLoc = content.index(self.cursorSelection)
+    else:
+      # select the next closest entry
+      self.cursorSelection = content[self.cursorLoc]
+    
+    return self.cursorSelection
+  
+  def handleKey(self, key, content, pageHeight):
+    """
+    Moves either the scroll or cursor according to the given input.
+    
+    Arguments:
+      key        - key code of user input
+      content    - displayed content
+      pageHeight - height of the display area for the content
+    """
+    
+    if self.isCursorEnabled:
+      self.getCursorSelection(content) # resets the cursor location
+      startLoc = self.cursorLoc
+    else: startLoc = self.scrollLoc
+    
+    newLoc = getScrollPosition(key, startLoc, pageHeight, len(content), self.isCursorEnabled)
+    if startLoc != newLoc:
+      if self.isCursorEnabled: self.cursorSelection = content[newLoc]
+      else: self.scrollLoc = newLoc
+      return True
+    else: return False
+
 def _getLabel(units, count, decimal, isLong):
   """
   Provides label corresponding to units of the highest significance in the



More information about the tor-commits mailing list