Issue 246: Extend MTInnerWindow to add aspect ratio controls
Reported by jaybradl...@gmail.com, Apr 28, 2010
Enhance the MTInnerWindow with controls for changing the aspect ratio of
the window just like any OS window works.

My first attempt is shown below. It works except for when rotated and
altering the size from any aspect control other than that at the top right
of the window.

# PYMT Plugin integration
IS_PYMT_PLUGIN = True
PLUGIN_TITLE = 'Napier Browser'
PLUGIN_AUTHOR = 'Jay Bradley'
PLUGIN_DESCRIPTION = 'Browse through file and search web resources'

from pymt import *
import os
import random
from OpenGL.GL import *

current_dir = os.path.dirname(__file__)
        
class MTNapierInnerWindow(MTInnerWindow):
  def __init__(self, **kwargs):
    print "MTNapierInnerWindow constructed"
    kwargs.setdefault('scale_min', 0.5)
    kwargs.setdefault('scale_max', 2)
    self.height_max = 600
    self.height_min = 100
    self.width_max = 600
    self.width_min = 200
    self.grow_delta = Vector(0, 0)
    self.aspect_controls_touches = {}
    MTInnerWindow.__init__(self, **kwargs)
    self.add_hold_aspect_ratio_controls()

  def fullscreen(self, *largs, **kwargs):
    self.hide_hold_aspect_ratio_controls()
    
    root_win = self.parent.get_parent_window()
    parent_widget = self.parent

    # save state for restore
    self.old_children = parent_widget.children
    self.old_size = self.size

    # set new children
    parent_widget.children = SafeList()
    rotation_to_nearest_90 = int(round(self.rotation / 90, 0)) * 90
    
    self.orientation_widget = MTScatterWidget(size = root_win.size,
rotation = rotation_to_nearest_90) # round rotation to nearest compass point
    
    if(rotation_to_nearest_90 == 0 or rotation_to_nearest_90 == 360):
      self.size = self.orientation_widget.size
      self.container.size = self.size
    elif(rotation_to_nearest_90 == 90):
      self.orientation_widget.pos = (self.orientation_widget.pos[0] +
self.orientation_widget.width, self.orientation_widget.pos[1])
      self.orientation_widget.size = (self.orientation_widget.size[1],
self.orientation_widget.size[0]) # swap height and width because it's on
its side
      self.size = self.orientation_widget.size
      self.container.size = self.size
    elif(rotation_to_nearest_90 == 180):
      self.orientation_widget.pos = (self.orientation_widget.pos[0] +
self.orientation_widget.width, self.orientation_widget.pos[1] +
self.orientation_widget.height)
      self.size = self.orientation_widget.size
      self.container.size = self.size
    elif(rotation_to_nearest_90 == 270):
      self.orientation_widget.pos = (self.orientation_widget.pos[0],
self.orientation_widget.pos[1] + self.orientation_widget.height)
      self.orientation_widget.size = (self.orientation_widget.size[1],
self.orientation_widget.size[0]) # swap height and width because it's on
its side
      self.size = self.orientation_widget.size
      self.container.size = self.size
    
    parent_widget.add_widget(self.orientation_widget)
    self.orientation_widget.add_widget(self.container)

    btn_unfullscreen = MTButton(pos=(self.orientation_widget.width-50,
self.orientation_widget.height-50), size=(50,50), label='Back')
    btn_unfullscreen.push_handlers(on_release=self.unfullscreen)
    self.orientation_widget.add_widget(btn_unfullscreen)
    
  def unfullscreen(self, *largs, **kwargs):
    # restore old widget
    parent_widget = self.parent
    
    parent_widget.children = self.old_children

    # reset container parent
    self.container.parent = self

    # set old size
    self.size = self.old_size
    self.container.size = self.size    
    
    self.show_hold_aspect_ratio_controls()
    
  def add_hold_aspect_ratio_controls(self):
    scaled_border = self.get_scaled_border()
    
    self.aspect_controls = MTWidget()
    self.add_widget(self.aspect_controls) # not sure if this is needed.
Works without it.

    self.aspect_control_bottom_left = MTButton(pos=(-scaled_border,
-scaled_border), size = (scaled_border, scaled_border),
cls="aspect_ratio_controls")
    self.aspect_controls.add_widget(self.aspect_control_bottom_left)
    
    self.aspect_control_top_left = MTButton(pos=(-scaled_border,
self.height), size = (scaled_border, scaled_border),
cls="aspect_ratio_controls")
    self.aspect_controls.add_widget(self.aspect_control_top_left)

    self.aspect_control_bottom_right = MTButton(pos=(self.width,
-scaled_border), size = (scaled_border, scaled_border),
cls="aspect_ratio_controls")
    self.aspect_controls.add_widget(self.aspect_control_bottom_right)
    
    self.aspect_control_top_right = MTButton(pos=(self.width, self.height),
size = (scaled_border, scaled_border), cls="aspect_ratio_controls")
    self.aspect_controls.add_widget(self.aspect_control_top_right)

  def hide_hold_aspect_ratio_controls(self):
    self.aspect_controls.visible = False
  
  def show_hold_aspect_ratio_controls(self):
    self.aspect_controls.visible = True
    
  def update_aspect_controls(self):
    scaled_border = self.get_scaled_border()
    
    scale_of_aspect_controls = 0.8
    size_of_aspect_controls = scaled_border * scale_of_aspect_controls
    
    self.aspect_control_bottom_left.pos = (-scaled_border *
scale_of_aspect_controls, -scaled_border * scale_of_aspect_controls)
    self.aspect_control_bottom_left.size = (size_of_aspect_controls,
size_of_aspect_controls)
    
    self.aspect_control_top_left.pos = (-scaled_border *
scale_of_aspect_controls, self.height)
    self.aspect_control_top_left.size = (size_of_aspect_controls,
size_of_aspect_controls)
    
    self.aspect_control_bottom_right.pos = (self.width, -scaled_border *
scale_of_aspect_controls)
    self.aspect_control_bottom_right.size = (size_of_aspect_controls,
size_of_aspect_controls)
    
    self.aspect_control_top_right.pos = (self.width, self.height)
    self.aspect_control_top_right.size = (size_of_aspect_controls,
size_of_aspect_controls)
  
  def on_touch_down(self, touch):
    if(len(self.aspect_controls_touches.keys()) == 0): # only one touch
allowed at a time on the aspect ratio controls
      local_touch = self.to_local(touch.x, touch.y)
      if(self.aspect_control_bottom_left.collide_point(local_touch[0],
local_touch[1])):
        #print "MTNapierInnerWindow touch down over bottom left aspect control"
        self.aspect_controls_touches[touch.id] = ["bottom", "left"]
      elif(self.aspect_control_top_left.collide_point(local_touch[0],
local_touch[1])):
        #print "MTNapierInnerWindow touch down over top left aspect control"
        self.aspect_controls_touches[touch.id] = ["top", "left"]
      elif(self.aspect_control_bottom_right.collide_point(local_touch[0],
local_touch[1])):
        #print "MTNapierInnerWindow touch down over bottom right aspect
control"
        self.aspect_controls_touches[touch.id] = ["bottom", "right"]
      elif(self.aspect_control_top_right.collide_point(local_touch[0],
local_touch[1])):
        #print "MTNapierInnerWindow touch down over top right aspect control"
        self.aspect_controls_touches[touch.id] = ["top", "right"]
      #else:
        #'print "MTNapierInnerWindow touch down NOT over aspect controls"
    MTInnerWindow.on_touch_down(self, touch)
  
  def on_touch_up(self, touch):
    # if touch in self.aspect_controls_touches then remove it
    if(touch.id in self.aspect_controls_touches.keys()):
      del self.aspect_controls_touches[touch.id]
    MTInnerWindow.on_touch_up(self, touch)
    
  def apply_angle_scale_trans(self, angle, scale, trans, point=Vector(0, 0)):

    if(len(self.aspect_controls_touches.keys()) >= 1 and len(self.touches)
== 1):  # change size rather than scale
      
      grow_delta = Vector(trans.x, trans.y)
      grow_delta = grow_delta.rotate(360.0 - self.rotation)
      
      aspect_control_location = self.aspect_controls_touches.items()[0][1]
      
      # handle switch in direction of growth      
      # left
      if("left" in aspect_control_location):
        grow_delta[0] *= -1.0
      # bottom
      if("bottom" in aspect_control_location):
        grow_delta[1] *= -1.0
      
      # change the size
      self.width += grow_delta[0]
      self.height += grow_delta[1]  
      
      # offset the window
      trans.x = 0
      trans.y = 0
      # left
      if("left" in aspect_control_location):
        trans.x = -grow_delta[0]
      # bottom
      if("bottom" in aspect_control_location):
        trans.y = -grow_delta[1]
      
      
      
      # check that the widget isn't too small or too large
      if(self.height <= self.height_min):
        self.height = self.height_min
      elif(self.height >= self.height_max):
        self.height = self.height_max
      if(self.width <= self.width_min):
        self.width = self.width_min
      elif(self.width >= self.width_max):
        self.width = self.width_max

      # resize the widget's container too
      self.container.height = self.height
      self.container.width = self.width

      #invalidate cached values for parent transform calucaltion
      self.__to_local = (-9999, 9999)
      self.__to_parent = (-9999, 9999)
      self.dispatch_event('on_transform', angle, scale, trans, point)
      
      scale = 0
      rotate = 0
        
    #else:
      # apply the changes as normal if not changing the aspect ratio
    MTScatterWidget.apply_angle_scale_trans(self, angle, scale, trans, point)
    
  def draw(self):
    self.update_aspect_controls()
    MTInnerWindow.draw(self)
    self.aspect_controls.dispatch_event('on_draw')


Aug 15, 2010
Project Member #1 txprog
(No comment was entered for this change.)
Labels: Type-Enhancement
Aug 19, 2010
Project Member #2 txprog
(No comment was entered for this change.)
Labels: Component-Widgets