changeset 7:119f063771bc

Added enso.ui.quasimode.window.
author Atul Varma <varmaa@toolness.com>
date Fri, 22 Feb 2008 06:56:58 -0600
parents 8cc56561b3a7
children 5283b9fedbbe
files enso/ui/quasimode/window.py
diffstat 1 files changed, 239 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/enso/ui/quasimode/window.py	Fri Feb 22 06:56:58 2008 -0600
@@ -0,0 +1,239 @@
+# Copyright (c) 2008, Humanized, Inc.
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#    1. Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#
+#    2. Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in the
+#       documentation and/or other materials provided with the distribution.
+#
+#    3. Neither the name of Enso nor the names of its contributors may
+#       be used to endorse or promote products derived from this
+#       software without specific prior written permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY Humanized, Inc. ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL Humanized, Inc. BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# ----------------------------------------------------------------------------
+#
+#   enso.ui.quasimode.window
+#
+# ----------------------------------------------------------------------------
+
+"""
+    Implements the quasimode's transparent window.
+
+    Throughout this module, it is important to keep in mind what the
+    various visual elements of the quasimode are.  Below is a mediocre
+    ASCII representation of those elements:
+
+      Description Text
+      user and auto-complete text
+      suggestion 1
+      suggestion 2
+      suggestion 3
+      suggestion 4
+      ...
+
+    The number of suggestions is limited by a constant declared in the
+    suggestion list module.
+
+    Each line of text is drawn by a separate "window", meaning that
+    there are actual separate transparent windows for each of them.
+    This allows a performance tweak: only those windows that have text
+    in them are actually drawn.
+"""
+
+# ----------------------------------------------------------------------------
+# Imports
+# ----------------------------------------------------------------------------
+
+import time
+
+from enso.graphics.measurement import pointsToPixels, pixelsToPoints
+
+from enso.ui.quasimode.linewindows import TextWindow
+from enso.ui.quasimode.layout import QuasimodeLayout
+from enso.ui.quasimode.layout import HEIGHT_FACTOR
+from enso.ui.quasimode.layout import DESCRIPTION_SCALE
+from enso.ui.quasimode.layout import AUTOCOMPLETE_SCALE, SUGGESTION_SCALE
+from enso.ui.quasimode.suggestionlist import MAX_SUGGESTIONS
+
+
+# ----------------------------------------------------------------------------
+# TheQuasimodeWindow
+# ----------------------------------------------------------------------------
+
+class TheQuasimodeWindow:
+    """
+    Implements the quasimode's display, in a multi-line transparent window.
+    """
+
+    # LONGTERM TODO: We will eventually need to deal with the overflow
+    # cases in the correct ways: the autocompletion/user text should
+    # wrap (as much as necessary), the suggestion list entries should
+    # (1) have a max size using ellipses and (2) should "vertically
+    # wrap" if there are more suggestions than will fit on one screen,
+    # the help text should have a max length with ellipsis
+        
+    def __init__( self ):
+        """
+        Instantiates the quasimode window, creating all the necessary
+        windows.
+        """
+
+        # Create a window for each line, keeping track of how tall
+        # that window is.  Use a "top" variable to know how far down
+        # the screen the top of the next window should start.
+
+        height = int( pointsToPixels( DESCRIPTION_SCALE[-1] )*HEIGHT_FACTOR )
+        self.__descriptionWindow = TextWindow(
+            height = height,
+            position = [ 0, 0 ],
+            )
+        top = height
+        
+        height = int( pointsToPixels( AUTOCOMPLETE_SCALE[-1] )*HEIGHT_FACTOR )
+        self.__userTextWindow = TextWindow(
+            height = height,
+            position = [ 0, top ],
+            )
+        top += height
+    
+        self.__suggestionWindows = []
+        for i in range( MAX_SUGGESTIONS ):
+            height = int( pointsToPixels( SUGGESTION_SCALE[-1] )*HEIGHT_FACTOR )
+            self.__suggestionWindows.append( TextWindow(
+                height = height,
+                position = [ 0, top ],
+                ) )
+            top += height
+
+        # The time, in float seconds since the epoch, when the last
+        # drawing of the quasimode display started.
+        self.__drawStart = 0
+
+
+    def update( self, quasimode, isFullRedraw ):
+        """
+        Fetches updated information from the quasimode, lays out and
+        draws the quasimode window.
+
+        This should only be called when the quasimode itself has
+        changed.
+
+        'isFullRedraw' is a boolean; if it is True, then the entire
+        quasimode display, including suggestion list, will be redrawn
+        when this function returns.  Otherwise, only the description
+        text and user text will be redrawn, and the suggestions will
+        be scheduled for redraw later.
+        """
+
+        # Instantiate a layout object, effectively laying out the
+        # quasimode display.
+        layout = QuasimodeLayout( quasimode )
+
+        self.__drawStart = time.time()
+
+        newLines = layout.newLines
+
+        self.__descriptionWindow.draw( newLines[0] )
+
+        suggestions = quasimode.getSuggestionList().getSuggestions()
+        if len( suggestions[0].toXml() ) == 0 \
+           and len( suggestions[0].getSource() ) == 0:
+            self.__userTextWindow.hide()
+        else:
+            self.__userTextWindow.draw( newLines[1] )
+
+        suggestionLines = newLines[2:]
+
+        # We now need to hide all line windows.
+        for i in range( len( suggestionLines ),
+                        len( self.__suggestionWindows ) ):
+            self.__suggestionWindows[i].hide()
+
+        self.__suggestionsLeft = _makeSuggestionIterator(
+            suggestionLines,
+            self.__suggestionWindows
+            )
+
+        if isFullRedraw:
+            while self.continueDrawing( ignoreTimeElapsed = True ):
+                pass
+
+
+    def continueDrawing( self, ignoreTimeElapsed = False ):
+        """
+        Continues drawing any parts of the quasimode display that
+        haven't yet been drawn, such as the suggestion list.
+
+        If 'ignoreTimeElapsed' is True, then the
+        TIME_UNTIL_SUGGESTIONS_SHOW constant will be ignored and any
+        pending suggestion waiting to be drawn will be rendered.
+
+        Returns whether a suggestion was drawn.
+
+        This function should only be called after update() has been
+        called.
+        """
+
+        # Amount of time, in seconds (float), to wait from the time
+        # that the quasimode begins drawing to the time that the
+        # suggestion list begins to be displayed.  Setting this to a
+        # value greater than 0 will effectively create a
+        # "spring-loaded suggestion list" behavior.
+        TIME_UNTIL_SUGGESTIONS_SHOW = 0.2
+
+        if self.__suggestionsLeft:
+            timeElapsed = time.time() - self.__drawStart
+            if ( (not ignoreTimeElapsed) and 
+                 (timeElapsed < TIME_UNTIL_SUGGESTIONS_SHOW) ):
+                return False
+            try:
+                suggestionDrawer = self.__suggestionsLeft.next()
+                suggestionDrawer.draw()
+                return True
+            except StopIteration:
+                self.__suggestionsLeft = None
+        return False
+
+
+class _SuggestionDrawer:
+    """
+    Private object encapsulating the rendering of a suggestion to a
+    suggestion window, useful for the delayed rendering of a
+    suggestion.
+    """
+
+    def __init__( self, line, suggestionWindow ):
+        self.__suggestionWindow = suggestionWindow
+        self.__line = line
+
+    def draw( self ):
+        self.__suggestionWindow.draw( self.__line )
+
+
+def _makeSuggestionIterator( lines, suggestionWindows ):
+    """
+    Returns a generator that provides _SuggestionDrawer objects for
+    each suggestion in the given suggestion lines, allowing each
+    suggestion line to be drawn to a respective suggestion window at a
+    later time.
+    """
+
+    for i in range( len(lines) ):
+        yield _SuggestionDrawer( lines[i],
+                                 suggestionWindows[i] )