29.3 Composite and Layout Panes

  • 29.3.1 Layout Pane Options
  • 29.3.2 Layout Pane Classes
  • 29.3.3 Scroller Pane Classes
  • 29.3.4 The Layout Protocol
  • This section describes the various composite and layout panes provided by CLIM, and the protocol that the layout panes obey. [annotate]

    The layout panes describe in this section are all composite panes that are responsible for positioning their children according to various layout rules. Layout panes can be selected in the same way as other panes using make-pane or make-instance. For convenience and readability of application pane layouts, many of these panes also provide a macro that expands into a make-pane form, passing a list of the panes created in the body of the macro as the :contents argument (described below). For example, you can express a layout of a vertical column of two label panes either as: [annotate]

    Note: Perhaps s/make-instance/make-pane/g [edit]-- Gilbert Baumann 2016-11-15 21:02Z
     

    (make-instance 'vbox-pane
      :contents (list (make-instance 'label-pane :text "One")
                      (make-instance 'label-pane :text "Two")))
    

    or as: [annotate]

    (vertically ()
      (make-instance 'label-pane :text "One")
      (make-instance 'label-pane :text "Two"))
    

    29.3.1 Layout Pane Options

    :contents   [Option]
              

    All of the layout pane classes accept the :contents options, which is used to specify the child panes to be laid out. [annotate]

    [annotate]

    :width   [Option]
    :max-width   [Option]
    :min-width   [Option]
    :height   [Option]
    :max-height   [Option]
    :min-height   [Option]
              

    These options control the space requirement paramaters for laying out the pane. The :width and :height options specify the preferred horizontal and vertical sizes. The :max-width and :max-height options specify the maximum amount of space that may be consumed by the pane, and give CLIM's pane layout engine permission to grow the pane beyond the preferred size. The :min-width and :min-height options specify the minimum amount of space that may be consumed by the pane, and give CLIM's pane layout engine permission to shrink the pane below the preferred size. [annotate]

    If either of the :max-width or :min-width options is not supplied, it defaults to the value of the :width option. If either of the :max-height or :min-height options is not supplied, it defaults to the value of the :height option. [annotate]

    :max-width, :min-width, :max-height, and :min-height can also be specified as a relative size by supplying a list of the form (number :relative). In this case, the number indicates the number of device units that the pane is willing to stretch or shrink. [annotate]

    The values of these options are specified in the same way as the :x-spacing and :y-spacing options to formatting-table. (Note that :character and :line may only be used on those panes that display text, such as a clim-stream-pane or a label-pane.) [annotate]

    [annotate]

    +fill+   [Constant]
              

    This constant can be used as a value to any of the relative size options. It indicates that pane's willingness to adjust an arbitrary amount in the specified direction. [annotate]

    [annotate]

    :align-x   [Option]
    :align-y   [Option]
              

    The :align-x option is one of :right, :center, or :left. The :align-y option is one of :top, :center, or :bottom. These are used to specify how child panes are aligned within the parent pane. These have the same semantics as for formatting-cell. [annotate]

    Note:

    Is the pane that this option is specified on the parent or the child pane here?

    The reference to formatting-cell is an interesting hint here. An :align-x or :align-y on a table cell specifies alignment of that cell within its parent.

    If we by that analogy say that an :align-x or :align-y option on a pane specifies the alignment of that pane within its parent pane, why don't gadgets have such an option?

    On the other hand, if we assume that :align-x or :align-y option on a pane specify the alignment of its children, the analogy to formatting-cell is flawed.

    [edit]-- Gilbert Baumann 2009-03-04 14:04Z
     

    [annotate]

    Note: This conclusion is wrong. formatting-cell specification says "how the _output_ in a cell will be aligned relative to other cells in the same table column" -- all cells in the row / column have the same size, alignment and spacing; we manipulate the output record that is a child of the cell. Given this interpretation all is consistent across different abstractions (gadgets included) - align-x/y specifies the pane/output record content's alignment. [edit]-- DK 2021-03-11 14:04Z
     
    Note:

    Dear DK,

    exactly this is my point. While in a table formatting-cell is around each cell, there is no such thing in the pane hierarchy for a layout pane. Layout panes don't wrap their children they layout into another pane.

    That is: (formatting-cell (t :align-x :center) ...foo...) says that ...foo... is centered. When I say (vertically () (vertically (:align-x :center) foo)), the questing was: What is aligned? Just foo, or the whole (vertically (:align-x :center) ...)?

    Your interpretation is valid, but would imply that the reference to formatting-cell is bogus. There is no equivalent to formatting-cell for layout panes. The equivalent would be to equate the pane that :align-x and :align-y are specified on with the cell.

    Hence your interpretation would imply that unlike with tables, each child of a vbox or hbox pane are aligned the very same. Two cells within a row or column could have different alignment. I was just asking if this reference to formatting-cell would hint to be able to specify different layout to two children of a layout pane, which would make perfect sense and is commonly offered by widget toolkits and at times called constraint resources. The latter a concept that CLIM completely misses here.

    Panes and gadgets try to expose a traditional toolkit to the application programmer.

    Also space requirement options like :min-width etc. also do not apply to the children.

    [edit]-- Gilbert Baumann 2022-02-05 20:43Z
     

    :x-spacing   [Option]
    :y-spacing   [Option]
    :spacing   [Option]
              

    These spacing options apply to hbox-pane, vbox-pane, table-pane, and grid-pane, and indicate the amount of horizontal and vertical spacing (respectively) to leave between the items in boxes or rows and columns in table. The values of these options are specified in the same way as the :x-spacing and :y-spacing options to formatting-table. :spacing specifies both the x and y spacing simultaneously. [annotate]

    [annotate]

    29.3.2 Layout Pane Classes

    hbox-pane   [Layout Pane]
    horizontally  (&rest options &key spacing &allow-other-keys ) &body contents [Macro]
              

    The hbox-pane class lays out all of its child panes horizontally, from left to right. The child panes are separated by the amount of space specified by spacing. [annotate]

    The horizontally macro serves as a convenient interface for creating an hbox-pane. [annotate]

    contents is one or more forms that are the child panes. Each form in contents is of the form: [annotate]

    • A pane. The pane is inserted at this point and its space requirements are used to compute the size. [annotate]
    • A number. The specified number of device units should be allocated at this point. [annotate]
    • The symbol +fill+. This means that an arbitrary amount of space can be absorbed at this point in the layout. [annotate]
    • A list whose first element is a number and whose second element evaluates to a pane. If the number is less than 1, then it means that that percentage of excess space or deficit should be allocated to the pane. If the number is greater than or equal to 1, then that many device units are allocated to the pane. For example: [annotate]

      (horizontally ()
        (1/3 (make-pane 'label-button-pane))
        (2/3 (make-pane 'label-button-pane)))
      

      would create a horizontal stack of two button panes. The first button takes one-third of the space, then second takes two-thirds of the space. [annotate]

      [annotate]

    [annotate]

    vbox-pane   [Layout Pane]
    vertically  (&rest options &key spacing &allow-other-keys ) &body contents [Macro]
              

    The vbox-pane class lays out all of its child panes vertically, from top to bottom. The child panes are separated by the amount of space specified by spacing. [annotate]

    The vertically macro serves as a convenient interface for creating an vbox-pane. [annotate]

    contents is as for horizontally. [annotate]

    [annotate]

    hrack-pane   [Layout Pane]
    vrack-pane   [Layout Pane]
              

    Similar to the hbox-pane and vbox-pane classes, except that these ensure that all children are the same size in the minor dimension. In other words, these panes are used to create stacks of same-sized items, such as menu items. [annotate]

    An hrack-pane is created when the :equalize-height option to horizontally is true. A vrack-pane is created when the :equalize-width option to vertically is true. [annotate]

    [annotate]

    table-pane   [Layout Pane]
    tabling  (&rest options) &body contents [Macro]
              

    This pane lays out its child panes in a two-dimensional table arrangement. Each of the table is specified by an extra level of list in contents. For example, [annotate]

    (tabling ()
      (list
        (make-pane 'label :text "Red")
        (make-pane 'label :text "Green")
        (make-pane 'label :text "Blue"))
      (list
        (make-pane 'label :text "Intensity")
        (make-pane 'label :text "Hue")
        (make-pane 'label :text "Saturation")))
    

    [annotate]

    Note:

    both lispworks and allegro revised later this macro to accept

    (tabling () ((make-pane …) (make-pane …)) ((make-pane …) (make-pane …)))

    instead.

    [edit]-- DK 2022-05-22 17:44Z
     

    grid-pane   [Layout Pane]
              

    A grid-pane is like a table-pane, except that each cell is the same size in each of the two dimensions. [annotate]

    [annotate]

    spacing-pane   [Layout Pane]
    spacing  (&rest options &key thickness &allow-other-keys ) &body contents [Macro]
              

    This pane reserves some margin space of thickness thickness around a single child pane. The space requirement keys that are passed in indicate the requirements for the surrounding space, not including the requirements of the child. [annotate]

    [annotate]

    outlined-pane   [Layout Pane]
    outlining  (&rest options &key thickness &allow-other-keys ) &body contents [Macro]
              

    This layout pane puts a outline of thickness thickness around its contents. [annotate]

    The :background option can be used to control the ink used to draw the background. [annotate]

    [annotate]

    restraining-pane   [Layout Pane]
    restraining  (&rest options) &body contents [Macro]
              

    Wraps the contents with a pane that prevents changes to the space requirements for contents from causing relayout of panes outside of the restraining context. In other words, it prevents the size constraints of the child from propagating up beyond this point. [annotate]

    [annotate]

    bboard-pane   [Layout Pane]
              

    A pane that allows its children to be any size and lays them out wherever they want to be (for example, a desktop manager). [annotate]

    [annotate]

    label-pane   [Service Pane]
    labelling  (&rest options &key label label-alignment &allow-other-keys ) &body contents [Macro]
              

    Creates a pane that consists of the specified label label, which is a string. label-alignment may be either :bottom or :top, which specifies whether the label should appear at the top or the bottom of the labelled pane. The default for label-alignment is left unspecified. [annotate]

    [annotate]

    29.3.3 Scroller Pane Classes

    CLIM defines the following additional pane classes, each having at least one implementation. [annotate]

    scroller-pane   [Service Pane]
    scrolling  (&rest options) &body contents [Macro]
              

    Creates a composite pane that allows the single child specified by contents to be scrolled. options may include a :scroll-bar option. The value of this option may be t (the default), which indicates that both horizontal and vertical scroll bars should be created; :vertical, which indicates that only a vertical scroll bar should be created; or :horizontal, which indicates that only a horizontal scroll bar should be created. [annotate]

    Note: For the scroller-pane, the option for displaying scroll bars is called :scroll-bar (no `s'), whereas for all the other panes it is called :scroll-bars. Surely, :scroll-bar is a typo. [edit]-- Robert Strandh 2017-11-27 17:31Z
     

    The pane created by the scrolling will include a scroller-pane that has as children the scroll bars and a viewport. The viewport of a pane is the area of the window's drawing plane that is currently visible to the user. The viewport has as its child the specified contents. [annotate]

    [annotate]

    pane-viewport  pane [Generic Function]
              

    If the pane pane is part of a scroller pane, this returns the viewport pane for pane. Otherwise it returns nil. [annotate]

    [annotate]

    pane-viewport-region  pane [Generic Function]
              

    If the pane pane is part of a scroller pane, this returns the region of the pane's viewport. Otherwise it returns nil. [annotate]

    Note:

    It isn't said in which coordinate system the viewport region should be returned. Most useful for the user however would be if the viewport would be returned in the coordinate system of 'pane'. So McCLIM just implements it that way.

    That is to be a little more precise:

    (defmethod pane-viewport-region ((pane basic-pane)) 
      (let ((viewport (pane-viewport pane))) 
        (and viewport 
             (untransform-region 
              (sheet-delta-transformation pane viewport) 
              (sheet-region viewport)))))

    [edit]-- Gilbert Baumann 2003-08-09 01:35Z
     

    [annotate]

    pane-scroller  pane [Generic Function]
              

    If the pane pane is part of a scroller pane, this returns the scroller pane itself. Otherwise it returns nil. [annotate]

    [annotate]

    scroll-extent  pane x y [Generic Function]
              

    If the pane pane is part of a scroller pane, this scrolls the pane in its viewport so that the position (x,y) of pane is at the upper-left corner of the viewport. Otherwise, it does nothing. [annotate]

    x and y are coordinates. [annotate]

    [annotate]

    29.3.4 The Layout Protocol

    The layout protocol is triggered by layout-frame, which is called when a frame is adopted by a frame manager. [annotate]

    Note: This is too simplistic. It could happen anytime that a frame is resized by the frame manager. It is correct that layout-frame is only called under our control, but there is a resize event handler on top level windows, which will invoke the layout protocol by allocate-space, too. [edit]-- Gilbert Baumann 2022-01-25 12:42Z
     

    CLIM uses a two pass algorithm to lay out a pane hierarchy. In the first pass (called called space composition), the top-level pane is asked how much space it requires. This may in turn lead to same the question being asked recursively of all the panes in the hierarchy, with the answers being composed to produce the top-level pane's answer. Each pane answers the query by returning a space requirement (or space-requirement) object, which specifies the pane's desired width and height as well as its willingness to shrink or grow along its width and height. [annotate]

    In the second pass (called space allocation), the frame manager attempts to obtain the required amount of space from the host window system. The top-level pane is allocated the space that is actually available. Each pane, in turn, allocates space recursively to each of its descendants in the hierarchy according to the pane's rules of composition. [annotate]

    Minor issue: It isn't alway possible to allocate the required space. What is the protocol for handling these space allocation failures? Some kind of error should be signalled when the constraints can't be satisfied, which can be handled by the application. Otherwise the panes will fall where they may. The define-application-frame macro should provide an option that allows programmers to conveniently specify a condition handler. --- ILA [annotate]

    For many types of panes, the application programmer can indicate the space requirements of the pane at creation time by using the space requirement options (described above), as well as by calling the change-space-requirements function (described below). For example, application panes are used to display application-specific information, so the application can determine how much space should normally be given to them. [annotate]

    Note:

    What is perhaps missing here is some function to notify CLIM that even though space requirements of a pane have not changes a relayout is needed nevertheless.

    At least I wish that something like this exists, so that relayout requests could be batched to be picked up by port event loop. Most toolkits and Free-CLIM do not like it, when allocate-space is called from outside the event loop.

    [edit]-- Gilbert Baumann 2022-01-25 12:37Z
     

    Other pane types automatically calculate how much space they need based on the information they need to display. For example, label panes automatically calculate their space requirement based on the text they need to display. [annotate]

    A composite pane calculates its space requirement based on the requirements of its children and its own particular rule for arranging them. For example, a pane that arranges its children in a vertical stack would return as its desired height the sum of the heights of its children. Note however that a composite is not required by the layout protocol to respect the space requests of its children; in fact, composite panes aren't even required to ask their children. [annotate]

    Space requirements are expressed for each of the two dimensions as a preferred size, a mininum size below which the pane cannot be shrunk, and a maxium size above which the pane cannot be grown. (The minimum and maximum sizes can also be specified as relative amounts.) All sizes are specified as a real number indicating the number of device units (such as pixels). [annotate]

    space-requirement   [Class]
              

    The protocol class of all space requirement objects. There are one or more subclasses of space-requirement with implementation-dependent names that implement space requirements. The exact names of these classes is explicitly unspecified. If you want to create a new class that behaves like a space requirement, it should be a subclass of space-requirement. All instantiable subclasses of space-requirement must obey the space requirement protocol. [annotate]

    All of the instantiable space requirement classes provided by CLIM are immutable. [annotate]

    [annotate]

    make-space-requirement  &key (width 0) (max-width 0) (min-width 0) (height 0) (max-height 0) (min-height 0) [Function]
              

    Constructs a space requirement object with the given characteristics, :width, :height, and so on. [annotate]

    Note: There seems to be some confusion about the precise default values for the minima and maxima. e.g. DUIM has min-width and max-width defaulting to width. We in our current implementation have min-width defaulting to 0 and max-width defaulting +fill+. [Note also that default values of 0 are pretty much nonsense]. [edit]-- GB 2003-01-23 23:07Z
     

    [annotate]

    space-requirement-width  space-req [Generic Function]
    space-requirement-min-width  space-req [Generic Function]
    space-requirement-max-width  space-req [Generic Function]
    space-requirement-height  space-req [Generic Function]
    space-requirement-min-height  space-req [Generic Function]
    space-requirement-max-height  space-req [Generic Function]
              

    These functions read the components of the space requirement space-req. [annotate]

    [annotate]

    space-requirement-components  space-req [Generic Function]
              

    Returns the components of the space requirement space-req as six values, the width, minimum width, maximum width, height, minimum height, and maximum height. [annotate]

    [annotate]

    space-requirement-combine  function sr1 sr2 [Function]
              

    Returns a new space requirement each of whose components is the result of applying the function function to each the components of the two space requirements sr1 and sr2. [annotate]

    function is a function of two arguments, both of which are real numbers. It has dynamic extent. [annotate]

    [annotate]

    space-requirement+  sr1 sr2 [Function]
              

    Returns a new space whose components are the sum of each of the components of sr1 and sr2. This could be implemented as follows: [annotate]

    (defun space-requirement+ (sr1 sr2)
      (space-requirement-combine #'+ sr1 sr2))
    

    [annotate]

    space-requirement+*  space-req &key width min-width max-width height min-height max-height [Function]
              

    Returns a new space requirement whose components are the sum of each of the components of space-req added to the appropriate keyword argument (for example, the width component of space-req is added to width). [annotate]

    This is intended to be a more efficient, "spread" version of space-requirement+. [annotate]

    [annotate]

    compose-space  pane &key width height [Generic Function]
              

    During the space composition pass, a composite pane will typically ask each of its children how much space it requires by calling compose-space. They answer by returning space-requirement objects. The composite will then form its own space requirement by composing the space requirements of its children according to its own rules for laying out its children. [annotate]

    The value returned by compose-space is a space requirement object that represents how much space the pane pane requires. [annotate]

    width and height are real numbers that the compose-space method for a pane may use as "recommended" values for the width and height of the pane. These are used to drive top-down layout. [annotate]

    [annotate]

    allocate-space  pane width height [Generic Function]
              

    During the space allocation pass, a composite pane will arrange its children within the available space and allocate space to them according to their space requirements and its own composition rules by calling allocate-space on each of the child panes. width and height are the width and height of pane in device units. [annotate]

    [annotate]

    Note: [Free-CLIM note] It is the responsibility of the caller of this function to resize a pane. A pane should never resize itself. [edit]-- Gilbert Baumann 2022-01-24 19:43Z
     

    change-space-requirements  pane &key resize-frame &rest space-req-keys [Generic Function]
              

    This function can be invoked to indicate that the space requirements for pane have changed. Any of the options that applied to the pane at creation time can be passed into this function as well. [annotate]

    resize-frame determines whether the frame should be resized to accommodate the new space requirement of the hierarchy. If resize-frame is true, then layout-frame will be invoked on the frame. If resize-frame is false, then the frame may or may not get resized depending on the pane hierarchy and the :resize-frame option that was supplied to define-application-frame. [annotate]

    [annotate]

    note-space-requirements-changed  sheet pane [Generic Function]
              

    This function is invoked whenever pane's space requirements have changed. sheet must be the parent of pane. Invoking this function essentially means that compose-space will be reinvoked on pane, then it will reply with a space requirement that is not equal to the reply that was given on the last call to compose-space. [annotate]

    This function is automatically invoked by change-space-requirements in the cases that layout-frame isn't invoked. In the case that layout-frame is invoked, it isn't necessary to call note-space-requirements-changed since a complete re-layout of the frame will be executed. [annotate]

    [annotate]

    changing-space-requirements  (&key resize-frame layout) &body body [Macro]
              

    This macro supports batching the invocation of the layout protocol by calls to change-space-requirements. Within the body, all calls to change-space-requirements change the internal structures of the pane and are recorded. When the body is exited, the layout protocol is invoked appropriately. body may have zero or more declarations as its first forms. [annotate]

    [annotate]

    Note:

    LAYOUT means: invoke LAYOUT-FRAME

    ... it does not mean (SETF FRAME-CURRENT-LAYOUT).

    [edit]-- DK 2022-05-26 18:29Z