home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Languages / Mops 2.7 / Mops source / Toolbox classes / View < prev    next >
Encoding:
Text File  |  1995-07-06  |  15.6 KB  |  517 lines  |  [TEXT/MSET]

  1. \ VIEW class.
  2.  
  3. \ Oct 91    mrh    Initial version.
  4. \ May 92    mrh    Support for "new-style" controls
  5. \ Feb 93    mrh    Added IDLE: method
  6. \ Sept 93    mrh    Enhanced along lines of Newton view scheme
  7.  
  8.  
  9. : PtInRect { x y rptr -- b }
  10.         word0 x y pack  rptr call PtInRect  i->l  ;
  11.  
  12.  
  13. \ VIEW is the generic superclass for everything that can be drawn in a
  14. \ window.  For example, all controls are now subclasses of view.
  15. \ In Mops a Window itself isn't a view, but contains one special view
  16. \ (the ContView) which covers the whole drawing area of the window.
  17.  
  18. \ In the view, we have an ivar which is a rect, ViewRect.  This is the
  19. \ rectangle defining the outer boundary of this view, relative to the
  20. \ current grafPort.  This rect is used by the view to set the clip
  21. \ region and the coordinate origin before drawing.
  22.  
  23. \ The ViewRect shouldn't now be set directly from a program (we don't have
  24. \ a SetViewRect: method any longer), but there is another rect, Bounds, which
  25. \ is intended to make things more convenient, since it defines the view's
  26. \ size and position relative to its parent or siblings.  Whenever the parent
  27. \ view is resized, it sends MOVED: to this view, and Bounds is then used in
  28. \ conjunction with the "justification" ivars, Ljust, Tjust, Rjust and Bjust,
  29. \ to set ViewRect.
  30.  
  31. \ Note that views are late-bound to, so can't appear within records.
  32.  
  33.  
  34. \ Here are the type definitions for the justification ivars:
  35.  
  36. type{    parLeft parRight parCenter    parProp    sibLeft    sibRight }
  37.         
  38. type{    parTop parBottom dummy        dummy    sibTop    sibBottom  }
  39.  
  40.  
  41.     0    value    MPOINT            \ Point where a click occurred
  42.         rect    LastSibRect
  43.  
  44.  
  45. \ Class PtrList is used for a list of pointers which needs to be expandable.
  46. \ We will use this to implement a view's list of its children, and also
  47. \ its list of controls.  We may eventually migrate it back into Mops.dic if
  48. \ it turns out to be useful enough.  Also we don't have a REMOVE: method
  49. \ yet -- put it in if you need it!
  50.  
  51.  
  52. \ :class    PTRLIST  super{ string  sequence }
  53. \
  54. \ :m ADD:        \ ( ptr -- )
  55. \    pad !  pad 4  add: super  ;m
  56. \
  57. \ :m FIRST?:
  58. \    size: super  NIF  false  EXIT  THEN        \ No elements - return false
  59. \    reset: super  ^1st: super @  true  ;m
  60. \
  61. \ :m NEXT?:    \ ( -- ptr T  |  -- F )
  62. \    4 skip: super  len: super  NIF  false  EXIT  THEN
  63. \    ^1st: super  @  true  ;m
  64. \    
  65. \ ;class
  66.  
  67.  
  68. \            ==============================
  69.  
  70. :class    VIEW    super{ object }  general        \ we late-bind to other views,
  71.                                                 \ thus we use "general".
  72. record
  73. {    rect        VIEWRECT        \ Bounding rectangle, rel to grafport.
  74.     rect        BOUNDS            \ We use this to set the viewRect
  75.     ptr            ^PARENT            \ Points to parent (containing) view
  76.     ptr            ^MyWIND            \ Points to owning window
  77. }
  78.     ptrList        CHILDREN        \ List of views that this one contains
  79.                                 \  it inherits from sequence so can't be
  80.                                 \  in a record
  81. record
  82. {    x-addr        DRAW            \ Draw handler
  83.     x-addr        ClickHndlr        \ Click handler
  84.     bool        ALIVE?
  85.     bool        ENABLED?
  86.     bool        WantsClicks?    \ True if we can accept clicks
  87.     bool        SetClip?        \ True if we need to set the clip (default)
  88.     bool        MeasureFromMe?    \ True if other siblings are to use this
  89.                                 \  view for sibling relative justification
  90.                                 \  modes
  91.     byte        #updates        \ Counts number of pending updates
  92.     byte        Ljust            \ Left justification
  93.     byte        Tjust            \ Top
  94.     byte        Rjust            \ Right
  95.     byte        Bjust            \ Bottom
  96. }
  97.  
  98. :m GETVIEWRECT:    get: viewRect    ;m
  99. :m GETRECT:        get: viewRect    ;m            \ a synonym for compatibility
  100.  
  101. :m ^VIEWRECT:    addr: viewRect    ;m            \ Needed for Scroller support
  102.  
  103.  
  104. \ Most views can be set up at compile time using setBounds: and setJust:.
  105. \ Then at run time addView: calls must be made to establish the parent-child
  106. \ relationships.
  107.  
  108. :m BOUNDS:        ( -- l t r b )        get: bounds  ;m
  109. :m GETBOUNDS:                        get: bounds  ;m        \ a synonym
  110.  
  111. :m SETBOUNDS:    ( l t r b -- )        put: bounds  ;m
  112.  
  113. :m GETJUST:    ( -- lj tj rj bj )    get: Ljust  get: Tjust
  114.                                 get: Rjust  get: Bjust  ;m
  115.                                 
  116. :m SETJUST:    ( lj tj rj bj -- )    put: Bjust  put: Rjust
  117.                                 put: Tjust  put: Ljust  ;m
  118.  
  119. :m MeasureFrom:  ( b -- )        put: measureFromMe?  ;m
  120.  
  121.  
  122. \ ADDVIEW: adds the passed-in view to this view's list of children.  This
  123. \ method must be called at run time, since pointers are used, and also it has
  124. \ to be called before NEW:, since NEW: assumes the list is set up already.
  125.  
  126. :m ADDVIEW:  { ^view -- }
  127.     ^view  add: children
  128.     ^base  setParent: [ ^view ]  ;m
  129.  
  130.  
  131. :m ENABLED?:    get: enabled?    ;m
  132. :m WINDOW:        get: ^MyWind    ;m
  133. :m SETWINDOW:    put: ^MyWind    ;m        \ Normally this is only called from
  134.                                         \  the window when setting up its
  135.                                         \  contView.
  136. :m WANTSCLICKS:    put: wantsClicks?  ;m
  137. :m SETCLICK:    put: ClickHndlr  true  put: wantsClicks?  ;m
  138. :m SETDRAW:        put: draw  ;m
  139. :m PARENT:        get: ^parent  ;m
  140. :m SETPARENT:    put: ^parent  ;m
  141.  
  142. \ UPDATE: generates an update event for the view, and CLEAR: erases it.  
  143.  
  144. :m UPDATE:    addr: viewRect  call InvalRect  ;m
  145. :m CLEAR:    clear: viewRect  ;m
  146.  
  147.  
  148. private            \ setting up for MOVED:
  149.  
  150. :m (>VR):  { left rt Pleft Prt Sleft Srt Ljust Rjust
  151.             \ cent left' rt' -- left' rt' }
  152.  
  153.     \ Note: we're saying "left" and "right" but this routine gets used
  154.     \ for top and bottom as well since the algorithm and justification
  155.     \ values are exactly the same.
  156.     
  157.     Ljust
  158.     SELECT[    parLeft        ]=>    left Pleft +    -> left'
  159.                         
  160.           [    parRight    ]=>    left Prt +        -> left'
  161.  
  162.           [    parCenter    ]=>    Pleft Prt + 2/    -> cent
  163.                               left cent +        -> left'
  164.                               
  165.           [    parProp        ]=> left
  166.                               Prt Pleft -  10000 */
  167.                               Pleft +            -> left'
  168.                               
  169.           [    sibLeft        ]=>    left sleft +    -> left'
  170.                               
  171.           [    sibRight    ]=>    left srt +        -> left'
  172.  
  173.           DEFAULT=>
  174.     ]SELECT
  175.           
  176.     Rjust
  177.     SELECT[ parLeft        ]=>    rt   Pleft +    -> rt'
  178.  
  179.           [    parRight    ]=>    rt     Prt +        -> rt'
  180.  
  181.           [    parCenter    ]=>    Pleft Prt + 2/    -> cent
  182.                               rt   cent +        -> rt'
  183.  
  184.           [    parProp        ]=> rt
  185.                               Prt Pleft -  10000 */
  186.                               Pleft +            -> rt'
  187.  
  188.           [    sibLeft        ]=>    rt     sleft +    -> rt'
  189.                               
  190.           [    sibRight    ]=>    rt     srt +        -> rt'
  191.  
  192.           DEFAULT=>
  193.     ]SELECT
  194.  
  195.     left' rt'
  196. ;m
  197.  
  198.  
  199. :m BOUNDS>VIEWRECT:  { \    bleft btop brt bbot
  200.                             pleft ptop prt pbot
  201.                             sleft stop srt sbot
  202.                             vleft vtop vrt vbot -- }
  203.  
  204. \ First, if this is a contView, there's no parent, so we just copy
  205. \ the bounds to the viewRect and get out.
  206.  
  207.     nil?: ^parent
  208.     IF        addr: bounds  ->: viewRect  EXIT  THEN
  209.                                             
  210.     getViewRect: [ get: ^parent ]                \ Parent's viewRect
  211.     -> pbot -> prt -> ptop -> pleft
  212.     get: lastSibRect  -> sbot  -> srt  -> stop  -> sleft
  213.     get: bounds          -> bbot  -> brt  -> btop  -> bleft
  214.     bleft brt  pleft prt  sleft srt  get: Ljust  get: Rjust  (>vr): self
  215.         -> vrt -> vleft
  216.     btop bbot  ptop pbot  stop sbot  get: Tjust  get: Bjust  (>vr): self
  217.         -> vbot -> vtop
  218.     vleft vtop vrt vbot  put: viewRect
  219.     get: measureFromMe?
  220.     IF    addr: viewRect  ->: lastSibRect  THEN
  221. ;m
  222.  
  223.  
  224. :m ChildrenMoved:  { \ l t r b -- }
  225.     get: lastSibRect
  226.     get: viewRect -> b -> r -> t -> l   r b l t  put: lastSibRect
  227.     BEGIN  each: children  WHILE  moved: []  REPEAT
  228.     put: lastSibRect  ;m
  229.  
  230.  
  231. public
  232.  
  233. \ MOVED: means that something has happened to change the position of
  234. \ this view (such as the parent view moving, or the bounds or justification
  235. \ parameters changing), so we must recompute the viewRect.  This means
  236. \ calling bounds>viewRect:, and calling MOVED: on our children as well.
  237.  
  238. \ The situation with the clip and updating is a bit tricky - at some stage
  239. \ we should set the clip to the new view position, and probably an update
  240. \ is needed eventually as well.  But in subclasses we might also have to
  241. \ erase the old position (Ctl does this, for example), which will probably
  242. \ be outside the new view area.  Also we might be inside a smaller parent
  243. \ view, so setting the clip might be inappropriate.  As well as this, we
  244. \ might have to do some other drawing which might make an update unnecessary.
  245.  
  246. \ So here in View, we just do the basic stuff, and don't set the clip
  247. \ or update.
  248.  
  249.  
  250. :m MOVED:
  251.     bounds>viewRect: self
  252.     get: alive? IF  childrenMoved: self  THEN
  253. ;m
  254.  
  255.  
  256. \ NEW:  ( -- )  fires up the view.  This method in normally called
  257. \ automatically from the owning window when the window gets a NEW:.
  258.  
  259. private            \ Here we just factor out some stuff which subclasses can
  260.                 \ call, since they'll usually need it, but not all at once.
  261.                 \ The setupNew: operations would normally be needed at the
  262.                 \ start (since we have to make the viewRect valid), and the
  263.                 \ windupNew: ops at the end, since NEW: may draw something,
  264.                 \ and the child views should normally be drawn after
  265.                 \ the parent (so they come out on top).  But this isn't a
  266.                 \ hard and fast rule, so we won't use callFirst/callLast
  267.                 \ here.
  268. :m SetupNew:
  269.     bounds>viewRect: self
  270.     NIL?: ^parent
  271.     NIF        get: ^parent  window: view  setWindow: self
  272.     THEN  ;m
  273.  
  274. :m WindupNew:
  275.     BEGIN  ( ^base )  each: children  WHILE  new: []  REPEAT
  276.     true  put: alive?  ;m
  277.  
  278. public
  279.  
  280. :m NEW:
  281.     setupNew: self   windupNew: self  ;m
  282.  
  283.  
  284. :m RELEASE:
  285.     BEGIN   each: children  WHILE  release: []  REPEAT
  286.     release: children
  287.     false  put: alive?   ;m
  288.  
  289.  
  290. \ DRAW: is the method called to get the view to draw itself.  There
  291. \ are a few subtleties.  Before drawing is done, we "focus" which means
  292. \ setting the clip region to viewRect, and the origin so that the top left
  293. \ corner of viewRect will be (0, 0).  Then after drawing, we need to call
  294. \ draw: for all the children.  Now here's the good part.  Both these jobs
  295. \ can be done via the CallFirst/CallLast mechanism, so the DRAW: method
  296. \ itself can just do the drawing.  Here in the View class itself, this just
  297. \ consists of executing the draw handler.
  298.  
  299. \ For some kinds of subview (notably Control), we may not want the origin
  300. \ change, but rather want the GrafPort origin.  In these views we can just
  301. \ put "0 call SetOrigin" before the drawing code.
  302.  
  303. \ Another useful point: when the draw handler is executed, tempRect will
  304. \ contain the bounding rectangle for the drawing, relative to the current
  305. \ origin.  This can be used to draw a frame, for example.
  306.  
  307. \ Final note: we DON'T clear the drawing area before calling the draw
  308. \ handler.  If you need it cleared, you can call CLEAR: self in the draw
  309. \ handler.
  310.  
  311. private
  312.  
  313. :m SetTempRect:  { \ left top rt bot -- }
  314.         \ Sets tempRect to a view-relative version of viewRect
  315.         \ -- we use this for a number of things.
  316.     get: viewRect  -> bot  -> rt  -> top  -> left
  317.     0  0  rt left -  bot top -  put: tempRect  ;m
  318.  
  319. public
  320.  
  321. \ SETCLIP: sets the clip before drawing.  This is a rather elaborate
  322. \ process, since we need to set the clip to the intersection of this
  323. \ view's viewRect and all its parent views' viewRects (which could possibly
  324. \ be smaller).  This can all be inhibited by setting SetClip? false
  325. \ (which we do when scrolling, for example, since the system has kindly
  326. \ set the clip for us already).
  327.  
  328. \ Note: when this method is called, the origin has been set so that the
  329. \ top left of this view is (0,0).  This is because we're going to use
  330. \ this origin for the drawing, and unless we use the same when we set the
  331. \ clip, the clip rectangle gets translated away somewhere strange!
  332.  
  333. \ This method has to be public since we late-bind to it.
  334.  
  335. :m SetClip: { \ ^view oLeft oTop left top rt bot -- }
  336.                                     \  Note: origin is rel to this view.
  337.     get: setClip?  NIF  true put: setClip?  EXIT  THEN
  338.     get: viewRect  -> bot  -> rt  -> top  -> left
  339.     left -> oLeft  top -> oTop            \ For origin adjustment later
  340.     get: ^parent -> ^view
  341.     BEGIN    ^view nilP <>
  342.     WHILE    ^view  getViewRect: view    \ Msg to class for speed
  343.         bot min -> bot  rt min -> rt  top max -> top  left max -> left
  344.         ^view parent: view  -> ^view
  345.     REPEAT
  346.     left oLeft -  top oTop -  rt oLeft -  bot oTop -  put: tempRect
  347.     addr: tempRect  call ClipRect  ;m
  348.  
  349. private
  350.  
  351. \ SetupDraw: is the equivalent of Focus() for a view in MacApp.
  352. \ Our CallFirst mechanism makes it automatic!
  353.  
  354. :m SetupDraw:  { \ left top rt bot -- port }
  355.     pushPort                    \ Save current port
  356.     get: ^myWind  set: window    \  and set right port for drawing
  357.     0  call SetOrigin
  358.     get: viewRect   -> bot  -> rt  -> top  -> left
  359.     left negate top negate   pack  call SetOrigin
  360.     get: setClip?
  361.     IF  setClip: [self]  ELSE  true put: setClip?  THEN
  362.     setTempRect: self  ;m
  363.  
  364. :m WindupDraw:        \ ( port -- )
  365.     BEGIN  each: children  WHILE  draw: []  REPEAT
  366.     0   call SetOrigin
  367.     0 put: #updates
  368.     popPort  ;m
  369.     
  370. public
  371.  
  372.  
  373. \ (DRAW): does the actual work for DRAW: - we do it this way so that
  374. \ subclasses can call (draw): super without triggering the callFirst
  375. \ and callLast code again.
  376.  
  377. :m (DRAW):    exec: draw  ;m
  378.  
  379.  
  380. callFirst    setupDraw:
  381. callLast    windupDraw:
  382.  
  383. :m DRAW:    (draw): self  ;m
  384.  
  385.  
  386.  
  387. :m IDLE:    \ Can be used in child views to call TEidle or whatever.
  388.     BEGIN  each: children  WHILE  idle: []  REPEAT  ;m 
  389.  
  390.  
  391. :m CLICK:    \ ( -- b )   Returns true if we've handled the click.
  392.  
  393. \ First we get straight out if we don't want clicks at all:
  394.  
  395.     get: wantsClicks?  NIF  false  EXIT  THEN
  396.  
  397. \ Now we get out if the click isn't in our own area.  Note that view
  398. \ subclasses can define "own area" however they like.  They could
  399. \ have several non-contiguous rects, regions, anything.
  400. \ Also note that we do this check before we check if the click was in one
  401. \ of our children.  This is significant, since our children can go outside
  402. \ our area (as with scrolling views).  But we don't want to respond to
  403. \ clicks in the "outside" part of one of our children, since this is a
  404. \ place in the child view which doesn't really exist from the user's point
  405. \ of view.
  406.  
  407.     where: fEvent  g->l  -> mpoint
  408.     mpoint  unpack  addr: viewRect  PtInRect
  409.     NIF  false  EXIT  THEN                \ not in our area - get out
  410.  
  411. \ OK, it's in our area.  We now look at our children first, since they
  412. \ must get first shot at the click:
  413.  
  414.     BEGIN    each: children
  415.     WHILE    click: []  IF  uneach: children  true  EXIT  THEN
  416.     REPEAT
  417.  
  418. \ If we got here, it wasn't in the children.  So we handle it here, and
  419. \ we're done:
  420.  
  421.     exec: clickHndlr  true  ;m
  422.  
  423.  
  424. :m KEY:        \ ( c -- )
  425.     BEGIN  dup  each: children  WHILE  key: []  REPEAT  drop  ;m
  426.  
  427.  
  428. :m ENABLE:
  429.     true put: enabled?
  430.     BEGIN  each: children  WHILE  enable: []  REPEAT  ;m 
  431.  
  432. :m DISABLE:
  433.     false put: enabled?
  434.     BEGIN  each: children  WHILE  disable: []  REPEAT  ;m
  435.     
  436.  
  437. :m MouseHere?:    \ ( -- b )  Returns true if the mouse is in this view.
  438.     where: theMouse  addr: viewRect  PtInRect  ;m
  439.  
  440. :m CLASSINIT:    
  441.     true put: wantsClicks?  true put: setClip?  ;m
  442.  
  443.  
  444. :m DUMP:  { \ l t r b -- }
  445.     ." view " .id: self  4 spaces
  446.     ." viewRect: "  getViewRect: self  -> b -> r -> t -> l
  447.     ." left: " l . ."  top: " t . ."   right: " r . ."  bottom: " b .  cr
  448.     first?: children  IF  ." children:"  cr  drop  THEN
  449.     BEGIN  each: children  WHILE  4 spaces  dump: []  REPEAT  ;m
  450.  
  451.  
  452. \ DRAWX: can be useful in debugging, when you want to see the view but
  453. \ don't have "real" drawing code yet.  It just draws a big X across
  454. \ the view area, joining the diagonally opposite corners.
  455.  
  456. :m DRAWX:  { \ l t r b -- }
  457.     get: tempRect  -> b  -> r  -> t  -> l
  458.     0 0 gotoxy  r b pack call LineTo
  459.     l b gotoxy  r 0 pack call LineTo  ;m
  460.  
  461. ;class
  462.  
  463.  
  464. endload
  465.  
  466. \ Testing:
  467.  
  468. view VV        \ Main view
  469. view C1        \ 5 child views
  470. view C2
  471. view C3
  472. view C4
  473. view C5
  474.  
  475. 40 40 300 200    setBounds: vv
  476.  
  477. 10 10 20 20        setBounds: c1
  478.                 true  measureFrom: c1
  479.  
  480. 2 0 30 60        setBounds: c2
  481.                 sibRight sibBottom 2dup  setJust: c2
  482.                 true  measureFrom: c2
  483.  
  484. 2 0 20 60        setBounds: c3
  485.                 sibRight sibBottom 2dup  setJust: c3
  486.                 true  measureFrom: c3
  487.                             
  488. 0 2 40 60        setBounds: c4
  489.                 sibRight sibBottom 2dup  setJust: c4
  490.                 true  measureFrom: c4
  491.                     
  492.  
  493.  
  494. : Drawit    draw: tempRect  ;        \ Draw handler for our views
  495.  
  496. : DrawVV    draw: vv  ;                \ Draw handler for fWind for test
  497.  
  498. : Clicked    noclip  ." clicked " .id: [self] cr  ;
  499.  
  500. : contentClick            \ New content click handler for fWind
  501.             click: vv  drop  ;
  502.             
  503. ' drawit    dup setDraw: vv  dup setDraw: c1  dup setDraw: c2
  504.             dup setDraw: c3  dup setDraw: c4  setDraw: c5
  505.  
  506. ' clicked    dup setclick: vv dup setclick: c1 dup setclick: c2
  507.             dup setclick: c3 dup setclick: c4 setclick: c5
  508.  
  509. : GO
  510.     cls
  511.     xts{ null null drawVV contentClick }  actions: fWind
  512.     c1 addview: vv  c2 addview: vv
  513.     c3 addview: vv  c4 addview: vv  c5 addview: vv
  514.     fWind  setWindow: vv    \ Normally done automatically from NEW: in Window+
  515.     new: vv                    \ Ditto
  516.     draw: vv  ;
  517.