Dynamically Managing the Layout of Controls

Anything beyond the basics in using the LiveCode language. Share your handlers, functions and magic here.

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

Post Reply
Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm

Dynamically Managing the Layout of Controls

Post by Bernard » Fri Dec 15, 2006 10:12 pm

Having just realised that I wasted a week building a table object, I thought I'd post here and ask if anyone has any suggestions for layout management.

Basically, I am building card elements dynamically, and the number of them can vary. I want to find some high-level way of controllilng their layout (including on re-sizing) without specifying the location of individual controls.

I can't find anything about this on the use-list. So, without knowing of any better solution I'm currently looking at doing something like the layout management using in Java's AWT.

Any suggestions/pointers?

Mark
Livecode Opensource Backer
Livecode Opensource Backer
Posts: 5150
Joined: Thu Feb 23, 2006 9:24 pm
Contact:

Post by Mark » Fri Dec 15, 2006 11:53 pm

Hi Bernard,

This is just a guess, but would it be possible for you to make a template group with all controls you need and copy or clone this group as many times as you need to display your table?

You can copy your template group into a "main" group with a vertical (and if necessary a horizontal) scrollbar. To make this as smooth as possible, follow these steps.

1) lock screen
2) copy template group into other "main" group
3) remember vscroll and hscroll
4) set vscroll and hscroll of main group to 0
5) position all "subgroups" relative to card size
6) set vscroll and hscroll to previous values

When you resize the stack, do step 3 up to and including 6.

Best,

Mark
The biggest LiveCode group on Facebook: https://www.facebook.com/groups/livecode.developers
The book "Programming LiveCode for the Real Beginner"! Get it here! http://tinyurl.com/book-livecode

marielle
Livecode Opensource Backer
Livecode Opensource Backer

Re: Dynamically Managing the Layout of Controls

Post by marielle » Sat Dec 16, 2006 11:27 am

Bernard wrote: I'm currently looking at doing something like the layout management using in Java's AWT.
If I post the code for this I already have somewhere public, would you agree to share any improvement you make? A good layout system would be a great addition to the language.

Marielle

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm

Post by Bernard » Sat Dec 16, 2006 12:12 pm

Hi Marielle,
If I post the code for this I already have somewhere public, would you agree to share any improvement you make?
Of course, that is only fair.

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm

Post by Bernard » Sat Dec 16, 2006 1:07 pm

Hi Mark
would it be possible for you to make a template group with all controls you need and copy or clone this group as many times as you need
Thanks for the suggestion. This might well help with one part of the layout. However, what I'm trying to think through is the idea of a more generic layout manager. In my mind, I think I need some way to set a property on a card, its groups, and their controls such that the groups and controls would lay themselves out in specific patterns.

Mark
Livecode Opensource Backer
Livecode Opensource Backer
Posts: 5150
Joined: Thu Feb 23, 2006 9:24 pm
Contact:

Post by Mark » Sat Dec 16, 2006 1:43 pm

Bernard, why?
-Mark
The biggest LiveCode group on Facebook: https://www.facebook.com/groups/livecode.developers
The book "Programming LiveCode for the Real Beginner"! Get it here! http://tinyurl.com/book-livecode

marielle
Livecode Opensource Backer
Livecode Opensource Backer

Post by marielle » Sat Dec 16, 2006 3:16 pm

Bernard wrote:Of course, that is only fair.
Good timing, next week I had planned to spend the time to clean out my reusable libraries. I should have something in a state good enough to share publicly (and for others to build upon) by middle of next week.

In the meantime, feel free to browser some I have about laying out options usually provided in most layout agents like java awt.

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 10055
Joined: Sat Apr 08, 2006 7:05 am
Contact:

Post by FourthWorld » Sat Dec 16, 2006 5:47 pm

Bernard wrote:In my mind, I think I need some way to set a property on a card, its groups, and their controls such that the groups and controls would lay themselves out in specific patterns.
See Rev's Geometry Manager.

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm

Post by Bernard » Sat Dec 16, 2006 6:07 pm

FourthWorld wrote:
Bernard wrote:In my mind, I think I need some way to set a property on a card, its groups, and their controls such that the groups and controls would lay themselves out in specific patterns.
See Rev's Geometry Manager.
Hi Richard, I'm not sure if the Geometry Manager will do what I want. I did have a look at it, and it appears to me that it is for controlling layout of objects visually within the IDE. I'm looking into the automatic layout of controls based on externally created data represented in cards and controls.

Unfortunately the chapter in the Rev 2.7 docs about the Geometry Manager is still empty, and the information on geometry management is fairly sparse even in the documentation from earlier versions of Rev. Also, I've been wary of even going down that path because I have read many complaints about the GM being broken.

Or are you suggesting that I dig into the internals of the GM to see how runrev are managing layouts?

Mark
Livecode Opensource Backer
Livecode Opensource Backer
Posts: 5150
Joined: Thu Feb 23, 2006 9:24 pm
Contact:

Post by Mark » Sat Dec 16, 2006 7:00 pm

Bernard, you didn't reply my question "why?" but I am sure that making a general solution for your problem is not recommendable. Instead, just make a script that creates and manages controls that the data fit in.

Mark
The biggest LiveCode group on Facebook: https://www.facebook.com/groups/livecode.developers
The book "Programming LiveCode for the Real Beginner"! Get it here! http://tinyurl.com/book-livecode

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm

Post by Bernard » Sat Dec 16, 2006 7:48 pm

Sorry Mark, I just missed your question. Basically, I have a rev app that is the presentation tier of a "web app". I would like the app to be "skinnable", and the skin would not only affect things like colours, fonts, etc. but also layout. As the number of fields and controls is variable, I'm hoping to find a generic solution.

I hope that makes it a bit clearer.

Since you say it is not recommended, I'm starting to think it is a difficult furrow to plough. I will reconsider it. Thanks for the warning :-)

marielle
Livecode Opensource Backer
Livecode Opensource Backer

Post by marielle » Tue Jan 16, 2007 9:18 pm

Got distracted by too many things. Here is a draft for a function for laying out elements (objects or groups) according to a box model.

Still need to test it, etc. but early feedback on the logic would be appreciated.

Code: Select all

/* ____________________________________________________________
|
|   Layout
|________________________________________________________
|
|   @Author:        Marielle  Lange
|   @Company:       Widged.com
|   @Date:          January 16, 2007
|   @Version:       0.1
|   @Changes since last version:  n/a 
|   @License:        Creative Commons Attribution 2.5 License  http://creativecommons.org/licenses/by/2.5/
|________________________________________________________
|
|   @Dependency:    -
|________________________________________________________ */
/*

  Description:
    Place one group exactly after another 

*/


#######################################################
#######################################################
###
###   API data
###
#######################################################
#######################################################
/*

   _ _ _ box _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
  !           Margin                            !
  !    __ list _____________________________    !
  !   |          padding                    |   !
  !   |    --- object -------------------   |   !
  !   |   '                              '  |   !
  !   |   '                              '  |   !
  !   |   '                              '  |   !
  !   |   '-------------------------------  |   !
  !   |    --- object -------------------   |   !
  !   |   '                              '  |   !
  !   |   '                              '  |   !
  !   |   '                              '  |   !
  !   |   '-------------------------------  |   !
  !   |    --- object -------------------   |   !
  !   |   '                              '  |   !
  !   |   '                              '  |   !
  !   |   '                              '  |   !
  !   |   '-------------------------------  |   !
  !   |_____________________________________|   !
  !_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _!

  --- box ------------------------
  box_layout:        table | proportional | grid -- taken from squeak, 
                                                 -- not sure what this means
  box_rectangle:     left,top,right,bottom
  box_dimensions:     width,height   
  box_topleft:       x,y          

  --- list ------------------------
  list_objectsRef:   list of the objects to lay out
  list_direction:    topToBottom | leftToRight
  list_margins:      n | n,n | n,n,n,n
  list_hAlignment:   top | bottom | middle
  list_vAlignment:   left | right | center
  list_hResizing:    shrinkWrap | spaceFill
  list_vResizing:    shrinkWrap

  --- object ------------------------
  object_paddings:   n | n,n | n,n,n,n

*/

#### Box ######################################

/* ____________________________________________________________
|
|  box_layout
|
*/
getProp box_layout
  put the box_layout of me into tValue
  put layout.force(tValue) into tValue
  return tValue
end box_layout

/* ____________________________________________________________
|
|  box_rectangle
|
*/
setProp box_rectangle pRect
  put rectangle.force(pRect) into tRect
  ----
  put abs(item 1 of tRect - item 3 of tRect) into tWidth
  put abs(item 2 of tRect - item 4 of tRect) into tHeight
  set the box_dimensions of me to tWidth,tHeight
  ----
  put item 1 of tRect into tLeft
  put item 2 of tRect into tTop
  set the box_topleft of me to tLeft,tTop
  ----
  return tValue
end box_rectangle
---
getProp box_rectangle
  put the box_rectangle of me into tValue
  return tValue
end box_rectangle

/* ____________________________________________________________
|
|  box_dimensions
|
*/
setProp box_dimensions pDimension
  ----
  put twoIntegers.force(pDimension) into tDimension
  ---
  put item 1 of tDimension into tWidth
  put item 2 of tDimension into tHeight
  --
  put the box_topleft of me into tTopleft
  put item 1 of tTopLeft into tLeft
  put item 2 of tTopLeft into tTop
  --
  put rectangle.FromDimensionAndTopLeft(tLeft,tTop,tWidth,tHeight) into tRect
  set the box_rectangle of me to tRect
  ----
  return tValue
end box_dimensions
----
getProp box_dimensions
  put the box_dimensions of me into tValue
  put twoIntegers.force(tValue) into tValue
  return tValue
end box_dimensions

/* ____________________________________________________________
|
|  box_topleft
|
*/
setProp box_topleft pTopleft
  ----
  put twoIntegers.force(pTopleft) into tTopLeft
  ----
  put item 1 of tTopLeft into tLeft
  put item 2 of tTopLeft into tTop
  --
  put the box_dimensions of me into tDimension
  put item 1 of tDimension into tWidth
  put item 2 of tDimension into tHeight
  --
  put rectangle.FromDimensionAndTopLeft(tLeft,tTop,tWidth,tHeight) into tRect
  set the box_rectangle of me to tRect
  ----
  return tValue
end box_topleft
----
getProp box_topleft
  put the box_dimensions of me into tValue
  put dimension.force(tValue) into tValue
  return tValue
end box_topleft



#### List ######################################


/* ____________________________________________________________
|
|  list_objectsRef
|
*/
getProp list_objectsRef
  put the list_objectsRef of me into tValue
  return tValue
end list_objectsRef

/* ____________________________________________________________
|
|  list_direction
|
*/
getProp list_direction
  put the list_direction of me into tValue
  put direction.force(tValue) into tValue
  return tValue
end list_direction

/* ____________________________________________________________
|
|  list_margins
|
*/
getProp list_margins
  put the list_margins of me into tValue
  put fourIntegers.force(tValue) into tValue
  return tValue
end list_margins

/* ____________________________________________________________
|
|  list_hResizing
|
*/
getProp list_hResizing
  put the list_hResizing of me into tValue
  put resizing.force(tValue) into tValue
  return tValue
end list_hResizing

/* ____________________________________________________________
|
|  list_vResizing
|
*/
getProp list_vResizing
  put the list_hResizing of me into tValue
  put resizing.force(tValue) into tValue
  return tValue
end list_vResizing


#### Objects ######################################

/* ____________________________________________________________
|
|  object_paddings
|
*/
getProp object_paddings
  put the object_paddings of me into tValue
  put fourIntegers.force(tValue) into tValue
  return tValue
end object_paddings


###################################################################################
###################################################################################
###
###  Indirect Properties
###
###################################################################################
###################################################################################

/* ____________________________________________________________
|
|  box.left/top/right/bottom/center
|
*/
function box.left
  return (item 1 of the box_rectangle of me)
end box.left
-----
function box.right
  return (item 3 of the box_rectangle of me)
end box.right
----
function box.top
  return (item 2 of the box_rectangle of me)
end box.top
----
function box.bottom
  return (item 4 of the box_rectangle of me)
end box.bottom
#########
function box.center
  put round(box.right()+box.left())/2 into tValue
  return tValue
end box.center
----
function box.middle
  put round(box.top()+box.bottom())/2 into tValue
  return tValue
end box.middle
----
function box.loc
  put box.center,box.middle into tValue
  return tValue
end box.loc

/* ____________________________________________________________
|
|  list.left/top/right/bottom/center
|
*/
function list.left
  put item 1 of the list_margins of me into tMargin
  put box.left() + tMargin into tValue
  return tValue
end list.left
-----
function list.right
  put item 3 of the list_margins of me into tMargin
  put box.right() - tMargin into tValue
  return tValue
end list.right
----
function list.top
  put item 3 of the list_margins of me into tMargin
  put box.top() + tMargin into tValue
  return tValue
end list.top
----
function list.bottom
  put item 4 of the list_margins of me into tMargin
  put box.bottom() - tMargin into tValue
  return tValue
end list.bottom
#######
function list.center
  put round(list.right()+list.left())/2 into tValue
  return tValue
end list.center
----
function list.middle
  put round(list.top()+list.bottom())/2 into tValue
  return tValue
end list.middle
---
function list.width
  put abs(list.left()-list.righ()) into tValue
  return tValue
end list.width
---
function list.height
  put abs(list.left()-list.righ()) into tValue
  return tValue
end list.height
----
function list.loc
  put list.center,list.middle into tValue
  return tValue
end list.loc

/* ____________________________________________________________
|
|  firstOrLastObject.left/top/right
|
*/
function firstOrLastObject.left
  put item 1 of the object_paddings of me into tPadding
  put list.left() + tPadding into tValue
  return tValue
end firstOrLastObject.left
-----
function firstOrLastObject.top
  put item 3 of the object_paddings of me into tPadding
  put list.top() + tPadding into tValue
  return tValue
end firstOrLastObject.top
-----
function firstOrLastObject.right
  put item 3 of the object_paddings of me into tPadding
  put list.right() - tPadding into tValue
  return tValue
end firstOrLastObject.right
----
function firstOrLastObject.bottom
  put item 4 of the object_paddings of me into tPadding
  put list.bottom() - tPadding into tValue
  return tValue
end firstOrLastObject.bottom
#############
function firstOrLastObject.center
  put round(firstOrLastObject.right()+firstOrLastObject.left())/2 into tValue
  return tValue
end firstOrLastObject.center
----
function firstOrLastObject.middle
  put round(firstOrLastObject.top()+firstOrLastObject.bottom())/2 into tValue
  return tValue
end firstOrLastObject.middle


###################################################################################
###################################################################################
###
###  Processing
###
###################################################################################
###################################################################################

/* ____________________________________________________________
|
|  objectList.layout
|  
|  Note: Resizing is being ignored for now
*/
on objectList.layout
  ----
  put the box_layout of me into tLayout
  --
  put the list_objectsRef of me into tLstObjects
  put the list_direction of me into tDirection
  put the list_hAlignment of me into thAlignment
  put the list_vAlignment of me into tvAlignment
  -- put the list_hResizing of me into thResizing
  -- put the list_vRezizing of me into tvResizing
  ----
  put the object_paddings of me into tPaddings
  ---
  put firstOrLastObject.top() into tTop
  put firstOrLastObject.left() into tLeft
  ---
  repeat with x = 1 to the number of lines in tLstObjects
    put (line x of tLstObjects) into tCtlRef
    if objectRef.validate(tCtlRef) is false then 
      -- TBD could add some warning message
      next repeat
    end if
    ---
    switch tDirection
      case "topToBottom" 
        object.hAlign tCtlRef, thAlignement
        set the top of tCtlRef to tTop
        add item 4 of tPaddings + item 2 of tPaddings to tTop
      break
      case "leftToRight"
        object.vAlign tCtlRef, tvAlignement
        set the left of tCtlRef to tLeft
        add item 3 of tPaddings + item 1 of tPaddings to tLeft
      break
    end switch
  end repeat
end objectList.layout

/* ____________________________________________________________
|
|  object.hAlign
|
*/
on object.hAlign tCtlRef, pAlign
  switch pAlign
  case "left"
    set the left of tCtlRef to firstOrLastObject.left()
    break
  case "right"
    set the right of tCtlRef to firstOrLastObject.right()
    break
  case "center"
    put the loc of tCtlRef into tLoc
    put firstOrLastObject.center() into tCenter
    put tCenter into item 1 of tLoc
    set the loc of tCtlRef to tLoc
    break
  end switch
end object.hAlign

/* ____________________________________________________________
|
|  object.vAlign
|
*/
on object.vAlign tCtlRef, pAlign
  switch pAlign
  case "top"
    set the top of tCtlRef to firstOrLastObject.top()
    break
  case "bottom"
    set the bottom of tCtlRef to firstOrLastObject.bottom()
    break
  case "middle"
    put the loc of tCtlRef into tLoc
    put firstOrLastObject.middle() into tMiddle
    put tMiddle into item 2 of tLoc
    set the loc of tCtlRef to tLoc
    break
  end switch
end object.vAlign


#######################################################
#######################################################
###
###   Dependencies
###
#######################################################
#######################################################

### container - card reference ##################

/* ____________________________________________________________
|
|  card.ref
|
*/
function card.ref
  put the container_ref of me into tRef
  if not exists(tRef) then 
    return empty  -- TBD: something more sensible
  end if
  put word -6 to -1 of the long id of tRef into tCardRef
  return tCardRef
end card.ref

### rectangle ###################################

/* ____________________________________________________________
|
|  rectangle.FromDimensionAndTopLeft
|
*/
function rectangle.FromDimensionAndTopLeft pLeft, pTop, pWidth, pHeight
  ----
  put integer.force(pLeft) into tLeft
  put integer.force(pTop) into tTop
  put integer.force(pWidth) into tWidth
  put integer.force(pHeight) into tHeight
  ----
  put tLeft, tTop, tLeft+tWidth, tTop+tHeight into tRect
  ----
  return tRect
end rectangle.FromDimensionAndTopLeft


###  Data Validation  ############################

/* ____________________________________________________________
|
|   objectRef.validate
|
*/
function objectRef.validate tRef
  if exists(tRef) then return true
  return false
end objectRef.validate

/* ____________________________________________________________
|
|  integer.force
|
*/
function integer.force pValue
  if pValue is not an integer then return 0
  return pValue
end integer.force

/* ____________________________________________________________
|
|  rectangle.force
|
*/
function rectangle.force pRect
  put pRect into tRect
  put integer.force(item 1 of tRect), integer.force(item 2 of tRect), integer.force(item 3 of tRect), integer.force(item 4 of tRect) into tRect
  return tRect
end rectangle.force

/* ____________________________________________________________
|
|  fourIntegers.force
|
*/
function fourIntegers.force pLstItems
  ----
  put the number of items of pLstItems into tNbItems
  -----
  put pLstItems into tLstItems
  if tNbItems = 1 then 
    put item 1 of tLstItems into item 2 of tLstItems
    put item 1 of tLstItems into item 3 of tLstItems
    put item 1 of tLstItems into item 4 of tLstItems
  else if tNbItems=2 then 
    put item 2 of tLstItems into item 3 of tLstItems
    put item 2 of tLstItems into item 4 of tLstItems
    put item 1 of tLstItems into item 2 of tLstItems
  end if
  put integer.force(item 1 of tLstItems), integer.force(item 2 of tLstItems), integer.force(item 3 of tLstItems), integer.force(item 4 of tLstItems) into tLstItems
  ----
  return tLstItems
  ----
end fourIntegers.force

/* ____________________________________________________________
|
|  twoIntegers.force
|
*/
function twoIntegers.force pItems
  ----
  put pLstItems into tLstItems
  put integer.force(item 1 of tLstItems), integer.force(item 2 of tLstItems) into tLstItems
  return tLstItems
  ----
end twoIntegers.force

/* ____________________________________________________________
|
|  layout.force
|
*/
function layout.force pLayout
  if pLayout is not among the items of "table,proportional,grid" then
    return "table"
  end if
  return pLayout
end layout.force

/* ____________________________________________________________
|
|  direction.force
|
*/
function direction.force pDirection
  if pDirection is not among the items of "leftToRight,topToBottom" then
    return "topToBottom"
  end if
  return pDirection
end direction.force

/* ____________________________________________________________
|
|  hAlign.force
|
*/
function hAlign.force pAlign
  if pAlign is not among the items of "left,center,right" then
    return "left"
  end if
  return pAlign
end hAlign.force

/* ____________________________________________________________
|
|  vAlign.force
|
*/
function vAlign.force pAlign
  if pAlign is not among the items of "top,bottom,middle" then
    return "top"
  end if
  return pAlign
end vAlign.force


/* ____________________________________________________________
|
|  resizing.force
|
*/
function resizing.force pResizing
  if pResizing is not among the items of "shrinkWrap,spaceFill" then
    return "spaceFill"
  end if
  return pResizing
end resizing.force




Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm

Post by Bernard » Wed Jan 17, 2007 10:22 am

Hi Marielle, thanks for sharing that. I'll have a look at it later today. Do I take from the reference to Squeak near the top that you got it from Squeak? (It never crossed my mind to have a look at what they were doing with layouts in Squeak).

Bernard

marielle
Livecode Opensource Backer
Livecode Opensource Backer

Post by marielle » Wed Jan 17, 2007 11:18 am

Bernard wrote:It never crossed my mind to have a look at what they were doing with layouts in Squeak).
This didn't cross my mind either :roll:. Then in the context of some other project of mine, I downloaded Alice, which let you construct 3D stories... and as it is written in Squeak, I got curious, downloaded the software, went through the manual and the concept of Morph. Interesting read. Though Squeak obviously hasn't caught up much. Their mailing list suggests that some of their founding members are now leaving the community.

I have a pile of papers on automatic layout generation... unfortunately, I won't have the time to go through it this month.

Marielle
Last edited by marielle on Wed Jan 17, 2007 1:06 pm, edited 1 time in total.

Bernard
Posts: 351
Joined: Sat Apr 08, 2006 10:14 pm

Post by Bernard » Wed Jan 17, 2007 1:00 pm

Their mailing list suggest that their founding members are now leaving the community
I didn't know that. That sounds very depressing. The whole dabbleDB project (http://dabbledb.com/) was built on Squeak - it won the Under the Radar competition last year (http://undertheradarblog.com/2006/03/03 ... winner-is/).

The main developer (Avi Bryant) of DabbleDB said that it would not have been possible to build it on any platform other than Smalltalk. And the whole thing runs inside Squeak.

Post Reply